Date: Fri, 30 May 2025 12:28:42 -0400
> On May 30, 2025, at 12:06 PM, Jeremy Rifkin via Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> Sorry, but this will never happen and should never happen. It’s completely outside the scope of the standard and even if it was his is something you should simply never do.
Agreed.
That said, the real core issue here is that the vtable layout is platform ABI, and the actual fix is for non-msvc compilers to generate code that conforms to the platform ABI.
An easier way to see this is to consider “long double”, on many x86_64 platforms long double is fp80, in the windows x86_64 abi long double is fp64. If this problem were incompatible behavior when a function with the type `void(long double)` was called no one would question it being the fault of the compiler that is not using the platform abi definition of long double. So it is with vtables.
Further, as others have said that standard does not mandate a vtable or even anything vtable like, just that polymorphic dispatch should occur, and which functions should be called. An implementation _could_ build its vtable implementation on top of objc’s dynamic dispatch (string hash based dispatch!), or it could use a side table to map allocation to “vtable”, or it could put every polymorphic object in it’s own allocation bucket and tie dispatch to the bucket being used. I’m sure people can come up with even more ways to do so if they really wanted to.
The fundamental issue here, is that if a platform’s ABI says “this is how C++ polymorphic dispatch is performed” it is the responsibility of the compiler to conform to that ABI if it wants to use the platform libraries.
There is no need for a standardized attribute that says “use this abi interop layer”, if nothing else it’s an increase in code size, yet another indirection in virtual dispatch, and using it would require pinning your binary to a point in the future where enough of your users had migrated to an OS that supported what is functionally a standard defined ABI.
—Oliver
>
> Jeremy
>
> On Fri, May 30, 2025 at 07:32 Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>> wrote:
>> On an x86_64 computer, we can build a DLL file using Visual Studio,
>> and then link it with an executable file built with GNU g++. We just
>> need to make sure that the same calling convention is used when the
>> executable invokes functions found inside the DLL.
>>
>> But if the DLL file returns a pointer to an abstract base class
>> object, something like:
>>
>> extern "C" IExaminer *CreateNewExaminer(void) noexcept;
>>
>> then we'll have a problem when we try to invoke methods on the
>> returned object because the Microsoft vtable might be different to
>> what the GNU g++ executable is expecting. The worse scenario will be
>> when 'IExaminer' inherits from a non-polymorphic base class, such as:
>>
>> struct Base {
>> int n;
>> };
>>
>> class IExaminer : public Base { . . . };
>>
>> Because when we have a pointer to an "IExaminer" object, the Microsoft
>> compiler will put the vtable pointer at address [base + 0x04], but the
>> GNU compiler will put the vtable pointer at address [base + 0x00].
>>
>> I'm just wondering if there's any possibility -- it's probably
>> unlikely -- but could we put something in the Standard to try
>> accommodate this?
>>
>> One workable solution not requiring any change to the Standard is to
>> create a second project in Visual Studio, and to put in it a
>> standalone function for each of IExaminer's methods:
>>
>> int IExaminer_GetStatus(IExaminer *const p) { return p->GetStatus(); }
>>
>> void IExaminer_SetAvailability(IExaminer *const p, bool const b) {
>> p->SetAvailability(b); }
>>
>> So then the GNU g++ executable would link with this second project and
>> use these stub functions to interact with the "IExaminer" object. A
>> tool could be written to automate this process of creating the second
>> project (maybe it's been done already?).
>>
>> Another solution, and this might be a bit far-fetched, might be to
>> standardise something that works along the following lines . . . You'd
>> have the following in your GNU g++ executable C++ source file:
>>
>> IExaminer *const p = CreateNewExaminer();
>>
>> and on the next line you would do:
>>
>> typedef std::abi< std::abi::Microsoft, IExaminer > IExaminer_Microsoft;
>> IExaminer_Microsoft *const pm = std::abi_wrap<IExaminer_Microsoft>(p);
>> int const status = pm->GetStatus();
>>
>> The function "std::abi_wrap" would return an object that you can use
>> exactly like an 'IExaminer' in your GNU executable, and internally it
>> would be something like:
>>
>> class IExaminer_Microsoft {
>> public:
>> typedef IExaminer type;
>> IExaminer *p;
>> int GetStatus(void) // Note that this is not virtual
>> {
>> // Adjust the vtable pointer for Microsoft:
>> char unsigned const *const pobj = static_cast<char
>> unsigned*>(static_cast<void*>(p));
>> typedef int (*FuncPtr)(IExaminer*);
>> FuncPtr *const vtable = static_cast<FuncPtr*>(
>> static_cast<void*>( pobj + 4 ) );
>> return vtable[3]( this->p ); // GetStatus is the 4th
>> entry in the vtable
>> }
>> };
>>
>> In order for the GNU compiler to be able to generate the class
>> IExaminer_Microsoft, it would need either one of two things:
>> (1) The GNU compiler would have to be programmed with how
>> Microsoft lays out classes -- including in cases of multiple virtual
>> inheritance, and would have to map it all across to how GNU does
>> things. I could see this code getting very complicated (although maybe
>> the latest version of ChatGPT/CoPilot or whatever could take a good
>> stab at it).
>> (2) The Microsoft compiler could be augmented to output such a
>> class. So you would invoke 'cl.exe' something like:
>>
>> cl.exe /input_file:IExaminer.hpp /input_class:IExaminer
>> /out_abi_class:IExaminer_Microsoft.hpp
>>
>> The Standard library could provide templates and classes such as
>> "std::abi" and "std::abit_wrap", and then it would be up to the
>> compiler implementors to add a new compiler option to output a class
>> like I've shown above.
>>
>> Before writing this email I had considered that this might be way
>> outside the scope of the Standard -- which doesn't by the way even
>> seem to acknowledge the existence of shared libraries -- but maybe
>> some of this could be standardised, such as the names and use of the
>> classes and functions like "std::abi" and "std::abi_wrap"?
>>
>> It would be really cool if C++29 compilers were expected to have a
>> feature whereby they would output an "ABI helper class" for any class
>> you specify, like the helper class I've shown about that finds the
>> vtable and manually invokes the right method. It would mean that
>> different compilers with different ABI's could work together with each
>> others' binaries more easily.
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]>
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
> Sorry, but this will never happen and should never happen. It’s completely outside the scope of the standard and even if it was his is something you should simply never do.
Agreed.
That said, the real core issue here is that the vtable layout is platform ABI, and the actual fix is for non-msvc compilers to generate code that conforms to the platform ABI.
An easier way to see this is to consider “long double”, on many x86_64 platforms long double is fp80, in the windows x86_64 abi long double is fp64. If this problem were incompatible behavior when a function with the type `void(long double)` was called no one would question it being the fault of the compiler that is not using the platform abi definition of long double. So it is with vtables.
Further, as others have said that standard does not mandate a vtable or even anything vtable like, just that polymorphic dispatch should occur, and which functions should be called. An implementation _could_ build its vtable implementation on top of objc’s dynamic dispatch (string hash based dispatch!), or it could use a side table to map allocation to “vtable”, or it could put every polymorphic object in it’s own allocation bucket and tie dispatch to the bucket being used. I’m sure people can come up with even more ways to do so if they really wanted to.
The fundamental issue here, is that if a platform’s ABI says “this is how C++ polymorphic dispatch is performed” it is the responsibility of the compiler to conform to that ABI if it wants to use the platform libraries.
There is no need for a standardized attribute that says “use this abi interop layer”, if nothing else it’s an increase in code size, yet another indirection in virtual dispatch, and using it would require pinning your binary to a point in the future where enough of your users had migrated to an OS that supported what is functionally a standard defined ABI.
—Oliver
>
> Jeremy
>
> On Fri, May 30, 2025 at 07:32 Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>> wrote:
>> On an x86_64 computer, we can build a DLL file using Visual Studio,
>> and then link it with an executable file built with GNU g++. We just
>> need to make sure that the same calling convention is used when the
>> executable invokes functions found inside the DLL.
>>
>> But if the DLL file returns a pointer to an abstract base class
>> object, something like:
>>
>> extern "C" IExaminer *CreateNewExaminer(void) noexcept;
>>
>> then we'll have a problem when we try to invoke methods on the
>> returned object because the Microsoft vtable might be different to
>> what the GNU g++ executable is expecting. The worse scenario will be
>> when 'IExaminer' inherits from a non-polymorphic base class, such as:
>>
>> struct Base {
>> int n;
>> };
>>
>> class IExaminer : public Base { . . . };
>>
>> Because when we have a pointer to an "IExaminer" object, the Microsoft
>> compiler will put the vtable pointer at address [base + 0x04], but the
>> GNU compiler will put the vtable pointer at address [base + 0x00].
>>
>> I'm just wondering if there's any possibility -- it's probably
>> unlikely -- but could we put something in the Standard to try
>> accommodate this?
>>
>> One workable solution not requiring any change to the Standard is to
>> create a second project in Visual Studio, and to put in it a
>> standalone function for each of IExaminer's methods:
>>
>> int IExaminer_GetStatus(IExaminer *const p) { return p->GetStatus(); }
>>
>> void IExaminer_SetAvailability(IExaminer *const p, bool const b) {
>> p->SetAvailability(b); }
>>
>> So then the GNU g++ executable would link with this second project and
>> use these stub functions to interact with the "IExaminer" object. A
>> tool could be written to automate this process of creating the second
>> project (maybe it's been done already?).
>>
>> Another solution, and this might be a bit far-fetched, might be to
>> standardise something that works along the following lines . . . You'd
>> have the following in your GNU g++ executable C++ source file:
>>
>> IExaminer *const p = CreateNewExaminer();
>>
>> and on the next line you would do:
>>
>> typedef std::abi< std::abi::Microsoft, IExaminer > IExaminer_Microsoft;
>> IExaminer_Microsoft *const pm = std::abi_wrap<IExaminer_Microsoft>(p);
>> int const status = pm->GetStatus();
>>
>> The function "std::abi_wrap" would return an object that you can use
>> exactly like an 'IExaminer' in your GNU executable, and internally it
>> would be something like:
>>
>> class IExaminer_Microsoft {
>> public:
>> typedef IExaminer type;
>> IExaminer *p;
>> int GetStatus(void) // Note that this is not virtual
>> {
>> // Adjust the vtable pointer for Microsoft:
>> char unsigned const *const pobj = static_cast<char
>> unsigned*>(static_cast<void*>(p));
>> typedef int (*FuncPtr)(IExaminer*);
>> FuncPtr *const vtable = static_cast<FuncPtr*>(
>> static_cast<void*>( pobj + 4 ) );
>> return vtable[3]( this->p ); // GetStatus is the 4th
>> entry in the vtable
>> }
>> };
>>
>> In order for the GNU compiler to be able to generate the class
>> IExaminer_Microsoft, it would need either one of two things:
>> (1) The GNU compiler would have to be programmed with how
>> Microsoft lays out classes -- including in cases of multiple virtual
>> inheritance, and would have to map it all across to how GNU does
>> things. I could see this code getting very complicated (although maybe
>> the latest version of ChatGPT/CoPilot or whatever could take a good
>> stab at it).
>> (2) The Microsoft compiler could be augmented to output such a
>> class. So you would invoke 'cl.exe' something like:
>>
>> cl.exe /input_file:IExaminer.hpp /input_class:IExaminer
>> /out_abi_class:IExaminer_Microsoft.hpp
>>
>> The Standard library could provide templates and classes such as
>> "std::abi" and "std::abit_wrap", and then it would be up to the
>> compiler implementors to add a new compiler option to output a class
>> like I've shown above.
>>
>> Before writing this email I had considered that this might be way
>> outside the scope of the Standard -- which doesn't by the way even
>> seem to acknowledge the existence of shared libraries -- but maybe
>> some of this could be standardised, such as the names and use of the
>> classes and functions like "std::abi" and "std::abi_wrap"?
>>
>> It would be really cool if C++29 compilers were expected to have a
>> feature whereby they would output an "ABI helper class" for any class
>> you specify, like the helper class I've shown about that finds the
>> vtable and manually invokes the right method. It would mean that
>> different compilers with different ABI's could work together with each
>> others' binaries more easily.
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]>
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
Received on 2025-05-30 16:28:56