Fir*_*cer 9 c++ compiler-construction dll binary-compatibility
有没有办法让不同编译器构建的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;
};
Run Code Online (Sandbox Code Playgroud)
test.cpp用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;
}
Run Code Online (Sandbox Code Playgroud)
a.cpp用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();
}
Run Code Online (Sandbox Code Playgroud)
编辑:我将以下行添加到GCC CreateClass方法,文本被正确打印到控制台,所以它的defenatly函数调用,这就是杀死它.
std::cout << "C.DLL Create Class" << std::endl;
Run Code Online (Sandbox Code Playgroud)
我想知道,COM如何设法维护甚至跨语言的二进制兼容性,因为它基本上所有具有继承性的类(尽管只有单个)因此也是虚函数.如果我不能重载运算符/函数,只要我能维护基本的OOP内容(即类和单继承),我就不会受到严重的打扰.
如果您降低期望并坚持简单的功能,您应该能够混合使用不同编译器构建的模块.
类和虚函数的行为方式由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);
Run Code Online (Sandbox Code Playgroud)
如果g ++编译器以与MSFT VC++不同的方式实现虚函数表 - 因为它可以自由地执行并且仍然符合C++标准 - 这只会像你演示的那样崩溃.VC++代码要求函数指针位于内存中的特定位置(相对于对象指针).
它通过继承变得更加复杂,并且真的,真的,复杂的多重继承和虚拟继承.
Microsoft一直非常公开VC++实现类的方式,因此您可以编写依赖于它的代码.例如,MSFT分发的许多COM对象头在头中都有C和C++绑定.C绑定暴露了他们的vtable结构,就像我上面的代码那样.
另一方面,GNU-IIRC已经开放了在不同版本中使用不同实现的选项,并且只保证用它的编译器构建的程序(仅!)将符合标准行为,
简短的回答是坚持简单的C风格函数,POD结构(普通旧数据;即没有虚函数),以及指向不透明对象的指针.
如果你这样做,你几乎肯定会遇到麻烦 - 而其他评论者认为C++ ABI在某些情况下可能是相同的,两个库使用不同的CRT,不同版本的STL,不同的异常抛出语义,不同优化......你正走向疯狂的道路.
小智 5
您可能能够组织代码的一种方法是在应用程序和 dll 中使用类,但将两者之间的接口保留为 extern "C" 函数。这是我使用 C# 程序集使用的 C++ dll 完成的方式。导出的 DLL 函数用于操作可通过静态类* Instance() 方法访问的实例,如下所示:
__declspec(dllexport) void PrintSomething()
{
(A::Instance())->PrintSometing();
}
Run Code Online (Sandbox Code Playgroud)
对于多个对象实例,使用 dll 函数创建实例并返回标识符,然后可以将标识符传递给 Instance() 方法以使用所需的特定对象。如果您需要应用程序和 dll 之间的继承,请在应用程序端创建一个类来包装导出的 dll 函数并从中派生其他类。像这样组织您的代码将使 DLL 接口简单且可在编译器和语言之间移植。
| 归档时间: |
|
| 查看次数: |
7266 次 |
| 最近记录: |