如何工作(可移植)与C ++类层次结构&动态链接库 [英] How to work (portably) with C++ class hierarchies & dynamic linked libraries

查看:152
本文介绍了如何工作(可移植)与C ++类层次结构&动态链接库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,所以我知道可移植性不是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 {
[.. baseinterfacedeclaration ..]
}

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

  class MyDllClass:public BaseClass {
[..这里的实际DLL类定义/实现...]
}

到目前为止,这么好。现在由于某些原因,我需要区分我的主要可执行文件在两种不同的BaseObjects - 说我有一个DescriptionClass和一个ActionClass,它们都是BaseClass,但有一个稍微不同的接口。我的第一个实现是简单地修改共享头并添加:

  class DECLSPEC DescriptionClass {
[。 。baseDescription interfacedeclaration ..]
}

class DECLSPEC ActionClass {
[.. baseAction interfacedeclaration ..]
}

然后我的DLL将变成:

  class MyDllClass:public ActionClass / *或DescriptionClass,取决于情况* / {
[..这里的实际DLL类定义/实现...]
}

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

  BaseClass * obj = CreateObjectFromDLL(path_to_dll); 
ActionClass * action_obj = dynamic_cast< ActionClass *>(obj);
if(action_obj){
//做与Action对象相关的东西
}
DescriptionClass * description_obj = dynamic_cast< ActionClass *>(obj);
if(description_obj){
//做与描述对象相关的东西
}

这里面的问题:虽然它在Windows上使用Visual Studio(可能是由于MS declspec扩展),它在Mac上失败(不知道现在如果它失败在调试,但我确保它在发布时失败)。原因很简单,即使不是立即明显:可执行&动态库是单独编译的。虽然它们都包括BaseClass,ActionClass,DescriptionClass的声明 - 这些类不是相同,它们只是存在于二进制和DLL中的相同的副本。所以实际上,我在DLL中创建的是一个dll'BaseClass *,这恰好与main'Baseclass *具有相同的内存布局,所以指针是兼容的,所以当我将指针从DLL传递到EXE时,所有作品如预期。 OTOH,当我去一个更复杂的类层次结构,dll'ActionClass和main'ActionClass的vtables / RTTI不再相同(虽然在源代码中它们是相同的),所以当我尝试做转换(通过dynamic_cast)一个主'baseClass *到main'ActionClass *我得到一个空结果 - >因为我的指针实际上指向一个dll'BaseClass对象/ dll'ActionClass对象,并且alghough在DLL我可以转换与没有问题BaseClass *一个ActionClass * - 在主可执行文件中,我无法将dll的BaseClass *转换为ActionClass *,因为DLL和主可执行文件版本的Action类之间存在微妙的运行时差异。 / p>

我已经通过在BaseClass(类似bool isActionClass())中声明一个虚函数来修复这个问题,所以现在我可以区分...对此解决方案不是很满意。



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

解决方案

我实际上找到了我的问题的答案,因为我需要完全制定它,我做了更好的google搜索:)
似乎是

  __ attribute __((dllimport))// import 
__attribute __((dllexport))// export

会试试,我想我也会在这里留下这个问题,以防有人绊倒这个问题根据编译器选项,包含在主二进制& DLL中的相同头文件通常会导致不同的实现,除非您在类上放置正确的dllimport / export属性。 / p>

Ok, so I know portability is not the strong point of C++, but I have to get my code running on both 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 - e.g., I have an abstract base class BaseClass; and I scan a given directory for DLLs, and for each DLL, I look for the factory method BaseClass* CreateObject(); - which returns a "BaseClass". I have a "shared header file" that I include both in the "main executable" & in the DLLs, that declares the BaseClass like this

#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:

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

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

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.

Is there something for GCC - e.g. some declaration similar to "__declspec" - a way to guarantee that the same class declared in "main executable" and in "dll" will be 100% compatible?

解决方案

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

这篇关于如何工作(可移植)与C ++类层次结构&amp;动态链接库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆