Is there some way to make c++ dlls built with diffrent compilers compatible with each other? The classes can have factory methods for creation and destruction, so each compiler can use its own new/delete (since diffrent runtimes have there own heaps).
I tried the following code but it crashed on the first member method:
interface.h
#pragma once
class IRefCounted
{
public:
virtual ~IRefCounted(){}
virtual void AddRef()=0;
virtual void Release()=0;
};
class IClass : public IRefCounted
{
public:
virtual ~IClass(){}
virtual void PrintSomething()=0;
};
test.cpp compiled with VC9, test.exe
#include "interface.h"
#include <iostream>
#include <windows.h>
int main()
{
HMODULE dll;
IClass* (*method)(void);
IClass *dllclass;
std::cout << "Loading a.dll\n";
dll = LoadLibraryW(L"a.dll");
method = (IClass* (*)(void))GetProcAddress(dll, "CreateClass");
dllclass = method();//works
dllclass->PrintSomething();//crash: Access violation writing location 0x00000004
dllclass->Release();
FreeLibrary(dll);
std::cout << "Done, press enter to exit." << std::endl;
std::cin.get();
return 0;
}
a.cpp compiled with g++ g++.exe -shared c.cpp -o c.dll
#include "interface.h"
#include <iostream>
class A : public IClass
{
unsigned refCnt;
public:
A():refCnt(1){}
virtual ~A()
{
if(refCnt)throw "Object deleted while refCnt non-zero!";
std::cout << "Bye from A.\n";
}
virtual void AddRef()
{
++refCnt;
}
virtual void Release()
{
if(!--refCnt)
delete this;
}
virtual void PrintSomething()
{
std::cout << "Hello World from A!" << std::endl;
}
};
extern "C" __declspec(dllexport) IClass* CreateClass()
{
return new A();
}
EDIT: I added the following line to the GCC CreateClass method, the text was correctly printed to the console, so its defenatly the function call thats killing it.
std::cout << "C.DLL Create Class" << std::endl;
I was wondering, how does COM manage to maintain binary compatibility even across languages, since its basicly all classes with inheritence (although only single) and therefore virtual functions. I'm not massivly bothered if I cant have overloaded operators/functions as long as I can maintain the basic OOP stuff (ie classes and single inheritence).
You should be able to mix modules built with different compilers if you lower your expectations and stick to simple functions.
The way classes and virtual functions behave is defined by the C++ standard, but the way that's implemented is up to the compiler. In this case, I know that VC++ builds objects which have virtual functions with a "vtable" pointer in the first 4 bytes of the object (I'm assuming 32-bit), and that points to a table of pointers to the method entry points.
So the line:
dllclass->PrintSomething();
is actually equivalent to something like:If the g++ compiler is implementing the virtual function tables in any way differently from MSFT VC++ -- as it is free to do and still be conformant to the C++ standard -- this will just crash as you've demonstrated. The VC++ code expects the function pointers to be in particular places in memory (relative to the object pointer).
It gets more complicated by inheritance, and really, really, complicated with multiple inheritance and virtual inheritance.
Microsoft has been very public about the way VC++ implements classes, so you can write code that depends on it. For example, a lot of COM object headers distributed by MSFT have both C and C++ bindings in the header. The C bindings expose their vtable structure like my code above does.
On the other hand, GNU -- IIRC -- has left open the option of using different implementations in different releases, and just guaranteeing the programs built with it's compiler (only!) will conform to the standard behaviour,
The short answer is to stick to simple C-style functions, POD structures (Plain Old Data; i.e., no virtual functions), and pointers to opaque objects.
I think you'll find this MSDN article useful
Anyway, from a quick glance at your code, I can tell you that you should not declare a virtual destructor in an interface. Instead, you need to do
delete this
within A::Release() when the ref count drops to zero.You do critically depend on the v-table layout being compatible between VC and GCC. That is somewhat likely to be okay. Ensuring that the calling convention matches is something you should check (COM: __stdcall, you: __thiscall).
Significantly, you are getting a AV on writing. Nothing is being written when you make the method call itself, so it is likely that operator<< is doing the bombing. Does std::cout get probably initialized by the GCC runtime when a DLL is loaded with LoadLibrary()? The debugger should tell.