Date: Fri, 12 Jul 2024 12:26:59 +0200
On 12/07/2024 00:57, Henry Miller wrote:
> There are many who have lost company politics and so cannot recompile their existing code. There is code from a third party where the code is owned and built by a third party and so every recompile will cost significant money (half of it paid to them, half in support costs getting the compiler to them - this second also implies significant opportunity cost lost because those people are not doing something else). There is even code where the source is lost and you can't rebuild it at any price. Unfortunately I don't see how this (or anything else) can address those problems.
True, but the goal is to address this for future code.
> There is a second problem: as time has passed programmers/C++ have learned. There are changes that it would be nice to make but it breaks ABI so we don't. Sometimes it is internal to the library and so an all new library can do better, sometimes it is the C++ standard that is not optimal. (AFAIK it is pure luck that std::vector is so cache friendly, but some future CPU may have some unknown change that makes it terrible and we would want to change). Of course you don't need std::stable::string you need std::string_two (or any other bike shed name). Again, I don't see who this helps - we might make some ABI changes before making stable::vector, but there is no particular reason to think that we will get the final ABI right for all time if we try today, so we can't put this into std::stable lest we be wrong.
The point of having std::stable classes is that they act as a common
interface. They are intended for use on public interfaces, not for use
throughout your software. You convert between your internal std::string
and std::stable::string whenever you pass through the interface.
So if some mythical future CPU works better if strings are implemented
as linked lists, you can just change std::string to use linked lists,
and if you recompile one of your libraries it won't immediately be
incompatible with all your other software, because in its public
interface it still presents the data as an array of char, same as
before. At that point the conversion from std::string (the linked list
version in the library) to std::stable::string will be much more
expensive, but at least it will still work.
If the cost of conversion becomes too high to bear a new stable type can
be introduced at that time.
> There is a third problem: interoperability. I'd love to have containers that I can share between C, C++, Rust, ada, zig, Go, Java, python (insert your favorite here) without a lot of effort writing wrappers. ABI isn't really an issue here, but C++'s name mangling and V- tables (which like ABI are not even in the C++ standard, but it is hard to implement C++ without) make it effectively impossible to use anything in std:: (D can apparently do this), Of course you can look up your platform name mangling rules and where the vtable lives (some platforms document this, others it is reverse engineer) and call any C++ function as a C function - nobody sane will allow this in production code though. Are you instead asking for some form of ISO::string, ISO::vector, ... - all new containers with a defined ABI such that you can implement and use them in any language?
Perhaps it would be worth taking a look at the paper, which answers all
of these questions. It suggests, as an initial list,
std::stable::string, std::stable::string_view, std::stable::vector, and
std::stable::span. It would probably also be good to add stable classes
for error handling.
I do not think more complex classes (std::map, std::unordered_map, etc.)
should have a stable counterpart: there is a high chance of divergence
between implementations, and the conversion between (for example)
std::unordered_map and std::stable::unordered_map is therefore always
going to be expensive (for a vector you can just move the pointer, but
for an unordered_map you'll have to rehash the entire thing. That's
unacceptable). Instead you'll have to provide a wrapper for such classes.
The paper proposes that stable classes must be standard layout types.
Standard layout types have no V-tables.
C++ already has a mechanism for disabling name mangling in the form of
extern "C".
> Those are the 3 different problems I see others talking about. They are different issues. In none of them do I see adding std::stable adding any value.
>
> Just remember, our predecessors were smart people too. While we know things they got wrong, we don't know how the world will change and thus what we will get wrong. That shouldn't stop us from trying to do better, but it should stop us from thinking we will get things perfect. Sometimes we won't even be able to agree on what better is.
The Honored Ancestors did not consider that barbarians would one day
distribute libraries in binary format. Thus, the idea that a change to a
class could have far-reaching consequences did not occur to them.
They also did not consider a significant difference between C and C++,
which is the following: C makes structs public by virtue of having them
in a public header, and keeps them private by having them in a private
header. However, C++, with its class-based design, pretty much mandates
that classes always exist in a public header, even though their
implementation is private. Programmers subconciously use the C rule of
"this thing exists in a public header and therefore it is a public class
that I can use in a public interface", but for C++ that makes no sense.
I'm really only proposing to fix this perception (and in doing so,
remove the reluctance to evolve standard library classes). The public
interface stuff is only there to make it more attractive to be used.
Hans Guijt
> There are many who have lost company politics and so cannot recompile their existing code. There is code from a third party where the code is owned and built by a third party and so every recompile will cost significant money (half of it paid to them, half in support costs getting the compiler to them - this second also implies significant opportunity cost lost because those people are not doing something else). There is even code where the source is lost and you can't rebuild it at any price. Unfortunately I don't see how this (or anything else) can address those problems.
True, but the goal is to address this for future code.
> There is a second problem: as time has passed programmers/C++ have learned. There are changes that it would be nice to make but it breaks ABI so we don't. Sometimes it is internal to the library and so an all new library can do better, sometimes it is the C++ standard that is not optimal. (AFAIK it is pure luck that std::vector is so cache friendly, but some future CPU may have some unknown change that makes it terrible and we would want to change). Of course you don't need std::stable::string you need std::string_two (or any other bike shed name). Again, I don't see who this helps - we might make some ABI changes before making stable::vector, but there is no particular reason to think that we will get the final ABI right for all time if we try today, so we can't put this into std::stable lest we be wrong.
The point of having std::stable classes is that they act as a common
interface. They are intended for use on public interfaces, not for use
throughout your software. You convert between your internal std::string
and std::stable::string whenever you pass through the interface.
So if some mythical future CPU works better if strings are implemented
as linked lists, you can just change std::string to use linked lists,
and if you recompile one of your libraries it won't immediately be
incompatible with all your other software, because in its public
interface it still presents the data as an array of char, same as
before. At that point the conversion from std::string (the linked list
version in the library) to std::stable::string will be much more
expensive, but at least it will still work.
If the cost of conversion becomes too high to bear a new stable type can
be introduced at that time.
> There is a third problem: interoperability. I'd love to have containers that I can share between C, C++, Rust, ada, zig, Go, Java, python (insert your favorite here) without a lot of effort writing wrappers. ABI isn't really an issue here, but C++'s name mangling and V- tables (which like ABI are not even in the C++ standard, but it is hard to implement C++ without) make it effectively impossible to use anything in std:: (D can apparently do this), Of course you can look up your platform name mangling rules and where the vtable lives (some platforms document this, others it is reverse engineer) and call any C++ function as a C function - nobody sane will allow this in production code though. Are you instead asking for some form of ISO::string, ISO::vector, ... - all new containers with a defined ABI such that you can implement and use them in any language?
Perhaps it would be worth taking a look at the paper, which answers all
of these questions. It suggests, as an initial list,
std::stable::string, std::stable::string_view, std::stable::vector, and
std::stable::span. It would probably also be good to add stable classes
for error handling.
I do not think more complex classes (std::map, std::unordered_map, etc.)
should have a stable counterpart: there is a high chance of divergence
between implementations, and the conversion between (for example)
std::unordered_map and std::stable::unordered_map is therefore always
going to be expensive (for a vector you can just move the pointer, but
for an unordered_map you'll have to rehash the entire thing. That's
unacceptable). Instead you'll have to provide a wrapper for such classes.
The paper proposes that stable classes must be standard layout types.
Standard layout types have no V-tables.
C++ already has a mechanism for disabling name mangling in the form of
extern "C".
> Those are the 3 different problems I see others talking about. They are different issues. In none of them do I see adding std::stable adding any value.
>
> Just remember, our predecessors were smart people too. While we know things they got wrong, we don't know how the world will change and thus what we will get wrong. That shouldn't stop us from trying to do better, but it should stop us from thinking we will get things perfect. Sometimes we won't even be able to agree on what better is.
The Honored Ancestors did not consider that barbarians would one day
distribute libraries in binary format. Thus, the idea that a change to a
class could have far-reaching consequences did not occur to them.
They also did not consider a significant difference between C and C++,
which is the following: C makes structs public by virtue of having them
in a public header, and keeps them private by having them in a private
header. However, C++, with its class-based design, pretty much mandates
that classes always exist in a public header, even though their
implementation is private. Programmers subconciously use the C rule of
"this thing exists in a public header and therefore it is a public class
that I can use in a public interface", but for C++ that makes no sense.
I'm really only proposing to fix this perception (and in doing so,
remove the reluctance to evolve standard library classes). The public
interface stuff is only there to make it more attractive to be used.
Hans Guijt
Received on 2024-07-12 10:27:00