Date: Fri, 12 Jul 2024 09:46:43 -0500
On Fri, Jul 12, 2024, at 05:26, Hans wrote:
> 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.
Only if we can predict the future and choose to address it. In the above company politics situation management knew in advance this politics was happening and decided that getting today's features out the door was more important than mitigating the cost of ABI, and so they intentionally did not tell engineering about the politics until it was too late to make changes. As an engineer of course I call this a bad decision, but those managers call it the right decision - who is right is not a knowable exercise.
The important take away is if [language/tools/god/whatever] doesn't force you to do the right thing in advance someone will get it wrong and we are back where we are today.
>> 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.
isn't that what char * and (struct*, size_t length) in extern "C" is? A standard abi for interfaces. However as above the hard part is realizing in advance where the interfaces are. I wouldn't want to make all functions calls go through C ABI just in case, that would perform badly and we lose all the nice things we get from C++ containers.
> 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.
But I want to use the linked-list string implementation where possible on this platform. Performance matters and I don't want to pay for the costs to convert to the standard ABI and back just because somebody might in the future change the ABI of string or vector. We have a history of the C++ library rarely changing ABI, so I'd even call that unlikely future - but unlikely does not mean never and so as someone who cares about ABI I should do this anyway.
Which is to say I want some sort of MAGIC so that if both sides do use the same ABI they don't go through the optimal API because I don't want to pay for what I'm not using.
> If the cost of conversion becomes too high to bear a new stable type can
> be introduced at that time.
Nothing stops us from introducing std2:: or whatever bikeshed name you want to give to the replacement type. ABI is hard because everyone needs to switch to that new type though - some want to switch everything instantly while others have what to them is good reason not to.
>> 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
> 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.
Only if we can predict the future and choose to address it. In the above company politics situation management knew in advance this politics was happening and decided that getting today's features out the door was more important than mitigating the cost of ABI, and so they intentionally did not tell engineering about the politics until it was too late to make changes. As an engineer of course I call this a bad decision, but those managers call it the right decision - who is right is not a knowable exercise.
The important take away is if [language/tools/god/whatever] doesn't force you to do the right thing in advance someone will get it wrong and we are back where we are today.
>> 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.
isn't that what char * and (struct*, size_t length) in extern "C" is? A standard abi for interfaces. However as above the hard part is realizing in advance where the interfaces are. I wouldn't want to make all functions calls go through C ABI just in case, that would perform badly and we lose all the nice things we get from C++ containers.
> 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.
But I want to use the linked-list string implementation where possible on this platform. Performance matters and I don't want to pay for the costs to convert to the standard ABI and back just because somebody might in the future change the ABI of string or vector. We have a history of the C++ library rarely changing ABI, so I'd even call that unlikely future - but unlikely does not mean never and so as someone who cares about ABI I should do this anyway.
Which is to say I want some sort of MAGIC so that if both sides do use the same ABI they don't go through the optimal API because I don't want to pay for what I'm not using.
> If the cost of conversion becomes too high to bear a new stable type can
> be introduced at that time.
Nothing stops us from introducing std2:: or whatever bikeshed name you want to give to the replacement type. ABI is hard because everyone needs to switch to that new type though - some want to switch everything instantly while others have what to them is good reason not to.
>> 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 14:47:07