Date: Fri, 30 May 2025 13:31:58 +0100
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.
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.
Received on 2025-05-30 12:32:08