简体   繁体   English

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

[英]How to work (portably) with C++ class hierarchies & dynamic linked libraries

Ok, so I know portability is not the strong point of C++, but I have to get my code running on both Mac&Windows. 好的,所以我知道可移植性不是C ++的强项,但我必须在Mac和Windows上运行我的代码。 I've come up with a solution, but it's not perfect, and I'm interested to see if there is someone out there who can suggest a better one. 我想出了一个解决方案,但它并不完美,而且我很想知道是否有人可以推荐更好的解决方案。

I need to us a class hierarchy in several DLLs/bundles - eg, I have an abstract base class BaseClass; 我需要在几个DLL / bundle中使用类层次结构 - 例如,我有一个抽象基类BaseClass; and I scan a given directory for DLLs, and for each DLL, I look for the factory method BaseClass* CreateObject(); 我扫描给定的目录中的DLL,并为每个DLL,我寻找工厂方法BaseClass * CreateObject(); - which returns a "BaseClass". - 返回“BaseClass”。 I have a "shared header file" that I include both in the "main executable" & in the DLLs, that declares the BaseClass like this 我有一个“共享头文件”,我包含在“主可执行文件”和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 .. ]
}

And then, in my DLL, I would typically include the BaseClass declaration, and declare my own "concrete" class: 然后,在我的DLL中,我通常会包含BaseClass声明,并声明我自己的“具体”类:

class MyDllClass:public BaseClass{
[.. actual DLL class definition/implementation here goes here ...]
}

So far, so good. 到现在为止还挺好。 Now for some reason, I need to differentiate in my main executable between two different kinds of BaseObjects - say I have a DescriptionClass and an ActionClass, both of which are BaseClass, but have a slightly different interface. 现在由于某种原因,我需要在两种不同类型的BaseObjects之间区分我的主要可执行文件 - 比如我有一个DescriptionClass和一个ActionClass,它们都是BaseClass,但是接口略有不同。 My fist implementation was to simply do modify the "shared header" and add: 我的第一个实现是简单地修改“共享头”并添加:

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

class DECLSPEC ActionClass{
  [.. base "Action interface" declaration .. ]
}

Then my DLL would become: 然后我的DLL将成为:

class MyDllClass:public ActionClass /* or DescriptionClass, depending on case*/ {
[.. actual DLL class definition/implementation here goes here ...]
}

And in my main executable, I would use it like this: 在我的主要可执行文件中,我会像这样使用它:

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
}

And herein lies the problem: although it works on Windows with Visual Studio (probably due to the MS declspec extension), it fails on Mac (not sure now if it fails on Debug, but I'm sure it fails on release) when compiled with GCC. 这里存在的问题是:虽然它可以在Windows上使用Visual Studio(可能是由于MS declspec扩展),但它在Mac上失败(现在不确定它是否在Debug上失败,但我确定它在发布时失败)在编译时与GCC。 The reason is simple, even if not immediately obvious: the executable & the dynamic library are compiled separately. 原因很简单,即使不是很明显:可执行文件和动态库是分开编译的。 Although they both include the declaration of BaseClass, ActionClass, DescriptionClass - these classes are not the same , they are just "identical copies" that are present in the binary and the DLL. 虽然它们都包含BaseClass,ActionClass,DescriptionClass的声明 - 这些类不一样 ,它们只是二进制文件和DLL中存在的“相同副本”。 So actually, what I'm creating in the DLL is a dll'BaseClass* , which happens to have the same memory layout with main'Baseclass*, so the pointers are compatible, so when I pass the pointer from DLL to EXE, it all works "as expected". 实际上,我在DLL中创建的是一个dll'BaseClass *,它恰好与main'Baseclass *具有相同的内存布局,因此指针是兼容的,所以当我将指针从DLL传递给EXE时,它所有工作“按预期”。 OTOH, when I go to a more complex class hierarchy, the vtables/RTTI of dll'ActionClass and main'ActionClass are no longer identical (although in source code they are identical), so when I try do convert (through dynamic_cast) a main'BaseClass* to a main'ActionClass* i get a null result -> because my pointer actually points to a dll'BaseClass object / dll'ActionClass object, and alghough in the DLL I can convert with no proble a "BaseClass*" into a "ActionClass*" - in the main executable, I can't convert the dll's "BaseClass*" into a "ActionClass*", due to the subtle runtime differences between the DLL's and the "main executable's" version of Action Class. OTOH,当我进入一个更复杂的类层次结构时,dll'ActionClass和main'ActionClass的vtable / RTTI不再相同(虽然在源代码中它们是相同的),所以当我尝试转换(通过dynamic_cast)一个主'BaseClass *到一个main'ActionClass *我得到一个空结果 - >因为我的指针实际上指向一个dll'BaseClass对象/ dll'ActionClass对象,而在DLL中我可以将没有问题的“BaseClass *”转换成一个“ActionClass *” - 在主可执行文件中,我无法将dll的“BaseClass *”转换为“ActionClass *”,因为DLL和ActionGroup的“主要可执行文件”版本之间存在细微的运行时差异。

I've "fixed" this by declaring a virtual method in the BaseClass (something like "bool isActionClass()" ), so now I can differentiate... but I'm not very happy with this solution. 我通过在BaseClass中声明一个虚拟方法来修复这个问题(类似于“bool isActionClass()”),所以现在我可以区分......但我对这个解决方案并不满意。

Is there something for GCC - eg some declaration similar to "__declspec" - a way to guarantee that the same class declared in "main executable" and in "dll" will be 100% compatible? GCC有什么东西 - 例如一些类似于“__declspec”的声明 - 一种保证在“主可执行文件”和“dll”中声明的同一类是100%兼容的方法吗?

I actually found the answer to my question due to the fact that I needed to formulate it completely, and I did a better google search :) It seems to be 我实际上找到了我的问题的答案,因为我需要完全制定它,我做了一个更好的谷歌搜索:)它似乎是

__attribute__((dllimport)) // import
__attribute__((dllexport)) // export

Will try that, I thought I'd leave the question here too, in case someone else stumbles on this problem (and as a warning to people that same header file included in a "main binary" & DLL will normaly lead to different actual implementation, depending on the compiler options - unless you put the proper dllimport/export attributes on the class). 试试这个,我想我也会在这里留下这个问题,以防其他人偶然发现这个问题(并且警告人们,“主二进制”和DLL中包含的相同头文件将会导致不同的实际实现,取决于编译器选项 - 除非你在类上放置适当的dllimport / export属性)。

That would only work if RTTI is enabled. 这只有在启用RTTI时才有效。 You, probably, get that automatically enabled in MSVC++, but gcc requires specific link switches. 您可能会在MSVC ++中自动启用,但gcc需要特定的链接开关。 Look in GCC FAQ for details. 有关详细信息,请参阅GCC FAQ

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM