views:

308

answers:

2

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?

+2  A: 

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

Virgil
+1  A: 

That would only work if RTTI is enabled. You, probably, get that automatically enabled in MSVC++, but gcc requires specific link switches. Look in GCC FAQ for details.

blinnov.com
True, but it was enabled, I forgot to mention that; but I did say that the dynamic cast works inside the shared library :)
Virgil
Well, I missed that. In that case you'll need to look for dllexport -like definitions, as you already mentioned
blinnov.com