当前位置:  开发笔记 > 编程语言 > 正文

编译器之间的DLL兼容性

如何解决《编译器之间的DLL兼容性》经验,为你挑选了2个好方法。

有没有办法让不同编译器构建的c ++ dll相互兼容?这些类可以有工厂方法来创建和销毁,因此每个编译器都可以使用自己的new/delete(因为不同的运行时有自己的堆).

我尝试了以下代码,但它崩溃了第一个成员方法:

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用VC9编译,test.exe

#include "interface.h"

#include 
#include 

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用g ++ g ++编译.exe -shared c.cpp -o c.dll

#include "interface.h"
#include 

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();
}

编辑:我将以下行添加到GCC CreateClass方法,文本被正确打印到控制台,所以它的defenatly函数调用,这就是杀死它.

std::cout << "C.DLL Create Class" << std::endl;

我想知道,COM如何设法维护甚至跨语言的二进制兼容性,因为它基本上所有具有继承性的类(尽管只有单个)因此也是虚函数.如果我不能重载运算符/函数,只要我能维护基本的OOP内容(即类和单继承),我就不会受到严重的打扰.



1> Die in Sente..:

如果您降低期望并坚持简单的功能,您应该能够混合使用不同编译器构建的模块.

类和虚函数的行为方式由C++标准定义,但实现的方式取决于编译器.在这种情况下,我知道VC++构建的对象具有虚拟函数,在对象的前4个字节中具有"vtable"指针(我假设是32位),并指向方法条目的指针表点.

这一行:dllclass->PrintSomething(); 实际上相当于:

struct IClassVTable {
    void (*pfIClassDTOR)           (Class IClass * this) 
    void (*pfIRefCountedAddRef)    (Class IRefCounted * this);
    void (*pfIRefCountedRelease)   (Class IRefCounted * this);
    void (*pfIClassPrintSomething) (Class IClass * this);
    ...
};
struct IClass {
    IClassVTable * pVTab;
};
(((struct IClass *) dllclass)->pVTab->pfIClassPrintSomething) (dllclass);

如果g ++编译器以与MSFT VC++不同的方式实现虚函数表 - 因为它可以自由地执行并且仍然符合C++标准 - 这只会像你演示的那样崩溃.VC++代码要求函数指针位于内存中的特定位置(相对于对象指针).

它通过继承变得更加复杂,并且真的,真的,复杂的多重继承和虚拟继承.

Microsoft一直非常公开VC++实现类的方式,因此您可以编写依赖于它的代码.例如,MSFT分发的许多COM对象头在头中都有C和C++绑定.C绑定暴露了他们的vtable结构,就像我上面的代码那样.

另一方面,GNU-IIRC已经开放了在不同版本中使用不同实现的选项,并且只保证用它的编译器构建的程序(仅!)将符合标准行为,

简短的回答是坚持简单的C风格函数,POD结构(普通旧数据;即没有虚函数),以及指向不透明对象的指针.



2> Ana Betts..:

如果你这样做,你几乎肯定会遇到麻烦 - 而其他评论者认为C++ ABI在某些情况下可能是相同的,两个库使用不同的CRT,不同版本的STL,不同的异常抛出语义,不同优化......你正走向疯狂的道路.

推荐阅读
农大军乐团_697
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有