On May 30, 2025, at 12:06 PM, Jeremy Rifkin via Std-Proposals <std-proposals@lists.isocpp.org> 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 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@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals