如何使用C++类层次结构和动态链接库(可移植)

Vir*_*gil 5 c++ gcc class shared-libraries

好的,所以我知道可移植性不是C++的强项,但我必须在Mac和Windows上运行我的代码.我想出了一个解决方案,但它并不完美,而且我很想知道是否有人可以推荐更好的解决方案.

我需要在几个DLL/bundle中使用类层次结构 - 例如,我有一个抽象基类BaseClass; 我扫描给定的目录中的DLL,并为每个DLL,我寻找工厂方法BaseClass*CreateObject(); - 返回"BaseClass".我有一个"共享头文件",我包含在"主可执行文件"和DLL中,它声明BaseClass像这样

#ifdef _MAC
 #define DECLSPEC 
#else
 #ifdef COMPILING_DLL
 #define DECLSPEC __declspec(dllexport)
 #else
 #define DECLSPEC __declspec(dllimport)
 #endif
#endif

class DECLSPEC BaseClass{
  [.. base "interface" declaration .. ]
}
Run Code Online (Sandbox Code Playgroud)

然后,在我的DLL中,我通常会包含BaseClass声明,并声明我自己的"具体"类:

class MyDllClass:public BaseClass{
[.. actual DLL class definition/implementation here goes here ...]
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.现在由于某种原因,我需要在两个不同类型的BaseObjects之间区分我的主要可执行文件 - 比如我有一个DescriptionClass和一个ActionClass,它们都是BaseClass,但是接口略有不同.我的第一个实现是简单地修改"共享头"并添加:

class DECLSPEC DescriptionClass{
  [.. base "Description interface" declaration .. ]
}

class DECLSPEC ActionClass{
  [.. base "Action interface" declaration .. ]
}
Run Code Online (Sandbox Code Playgroud)

然后我的DLL将成为:

class MyDllClass:public ActionClass /* or DescriptionClass, depending on case*/ {
[.. actual DLL class definition/implementation here goes here ...]
}
Run Code Online (Sandbox Code Playgroud)

在我的主要可执行文件中,我会像这样使用它:

BaseClass* obj = CreateObjectFromDLL("path_to_dll");
ActionClass* action_obj = dynamic_cast<ActionClass*>(obj);
if(action_obj){
   // Do the stuff that is relevant for Action objects
}
DescriptionClass* description_obj = dynamic_cast<ActionClass*>(obj);
if(description_obj){
   // Do the stuff that is relevant for Description objects
}
Run Code Online (Sandbox Code Playgroud)

这里存在的问题是:虽然它可以在Windows上使用Visual Studio(可能是由于MS declspec扩展),但它在Mac上失败(现在不确定它是否在Debug上失败,但我确定它在发布时失败)在编译时与GCC.原因很简单,即使不是很明显:可执行文件和动态库是分开编译的.虽然它们都包含BaseClass,ActionClass,DescriptionClass的声明 - 这些类不一样,它们只是二进制文件和DLL中存在的"相同副本".实际上,我在DLL中创建的是一个dll'BaseClass*,它恰好与main'Baseclass*具有相同的内存布局,因此指针是兼容的,所以当我将指针从DLL传递给EXE时,它所有工作"按预期".OTOH,当我进入一个更复杂的类层次结构时,dll'ActionClass和main'ActionClass的vtable/RTTI不再相同(虽然在源代码中它们是相同的),所以当我尝试转换(通过dynamic_cast)一个主'BaseClass*到一个main'ActionClass*我得到一个null结果 - >因为我的指针实际上指向一个dll'BaseClass对象/ dll'ActionClass对象,而在DLL中我可以将没有问题的"BaseClass*"转换成一个"ActionClass*" - 在主可执行文件中,我无法将dll的"BaseClass*"转换为"ActionClass*",因为DLL和ActionGroup的"主要可执行文件"版本之间存在细微的运行时差异.

我通过在BaseClass中声明一个虚拟方法来修复这个问题(类似于"bool isActionClass()"),所以现在我可以区分......但我对这个解决方案并不满意.

GCC有什么东西 - 例如一些类似于"__declspec"的声明 - 一种保证在"主可执行文件"和"dll"中声明的同一类是100%兼容的方法吗?

Vir*_*gil 3

我实际上找到了我的问题的答案,因为我需要完整地表述它,并且我做了更好的谷歌搜索:)似乎是

__attribute__((dllimport)) // import
__attribute__((dllexport)) // export
Run Code Online (Sandbox Code Playgroud)

会尝试一下,我想我也会把问题留在这里,以防其他人偶然发现这个问题(并且警告人们,“主二进制文件”和 DLL 中包含的相同头文件通常会导致不同的实际实现,取决于编译器选项 - 除非您在类上放置了正确的 dllimport/export 属性)。