Dll compatibility between compilers

2019-02-09 07:50发布

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).

9条回答
劫难
2楼-- · 2019-02-09 08:04

You're almost certainly asking for trouble if you do this - while other commenters are correct that the C++ ABI may be the same in some instances, the two libraries are using different CRTs, different versions of the STL, different exception throwing semantics, different optimizations... you're heading down a path towards madness.

查看更多
Anthone
3楼-- · 2019-02-09 08:04

interesting.. what happens if you compile the dll in VC++ as well, and what if you put some debug statements in CreateClass()?

I'd say its possible that your 2 different runtime 'versions' of cout are conflicting instead of your method call - but I trust that the returned function pointer/dllclass isn't 0x00000004?

查看更多
贼婆χ
4楼-- · 2019-02-09 08:05

Your problem is maintaining ABI. Though same compiler but different versions you still want to maintain the ABI. COM is one way of solving it. If you really want to understand how COM solves this then check out this article CPP to COM in msdn which describes the essence of COM.

Apart from COM, there are other (one of the oldest) ways to solve ABI like using Plain old data and opaque pointers. Look at Qt/KDE library developers way of solving ABI.

查看更多
做个烂人
5楼-- · 2019-02-09 08:05

The problem with your code that causes the crash is virtual destructors in the interface definition:

virtual ~IRefCounted(){}
    ...
virtual ~IClass(){}

Remove them and everything is going to be ok. The problem is caused by the way the virtual function tables are organized. MSVC compiler ignores the destructor, but GCC adds it as a first function in the table.

Have a look at the COM interfaces. They don't have any constructors/destructors. Never define any destructors in the interface and it's going to be ok.

查看更多
Luminary・发光体
6楼-- · 2019-02-09 08:09

You can as long as you only use extern "C" functions.

This is because the "C" ABI is well defined, while the C++ ABI is deliberately not defined. Thus each compiler is allowed to define its own.

In some compilers the C++ ABI between different versions of the compiler or even with different flags will generate an incompatable ABI.

查看更多
Animai°情兽
7楼-- · 2019-02-09 08:16

One way you might be able to organize the code is to use classes in both the app and the dll, but keep the interface between the two as extern "C" functions. This is the way I have done it with C++ dlls used by C# assemblies. The exported DLL functions are used to manipulate instances accessible via static class* Instance() methods like this:

__declspec(dllexport) void PrintSomething()
{
    (A::Instance())->PrintSometing();
}

For multiple instances of objects, have a dll function create the instance and return the identifier, which can then be passed to the Instance() method to use the specific object needed. If you need inheritance between the app and the dll, create a class on the app side that wraps the exported dll functions and derive your other classes from that. Organizing your code like this will keep the DLL interface simple and portable between both compilers and languages.

查看更多
登录 后发表回答