Writing shared c++-libraries that are useable by different compiler systems

One thing one has to consider when writing DLLs or shared objects that should be usable across different compiler systems is the name decoration: C++-compilers resolve symbols to be imported not only by name but have to add extra information to the symbol name to enable function-overloading. That information typically contains information about the namespace the symbol is defined in as well as argument and return types if the symbol refers to a function. What one has to take care about is that the encoding of that information is not standartized. So symbols exported from a naively coded C++-dll will likely not be found by another C++-compiler system that tries to import them. A solution to the problem is to export only pure-virtual interfaces (which do not result in any exported symbols by themselves) and functions exported declared as extern "C". extern "C" tells the compiler to use c-style name mangling which exports the symbols merely by name. That implies you cannot use function overloading for that functions but that won't be necessary:

An example

Consider you want to build a DLL that should be useable with different compiler-systems. You can define the functions to be exported in a DLL-interface-class like this
__ dllinterface.h __

class ISomeDll
{
public:
  virtual class ISomeExportedClass* anExportedFunction() const = 0;
  /* ... */
};
Classes to be exported need to be split up into pure-virtual interfaces and their implementations like this
__ dllinterface.h __
class ISomeExportedClass
{
public:
  virtual ~ISomeExportedClass() {}
  virtual int aMethod() = 0;
};
__ exported_class.h _
#include "dllinterface.h"

class CSomeExportedClass : public ISomeExportedClass
{
public:
  virtual int aMethod() { return 0; }
};
Note that the dll-interface (ISomeDll) as well as the exported classes (here ISomeExportedClass) may not contain any functions that are nonvirtual: Those would not be called by using the virtual function table but by resolving their adresses at link-time, which ain't possible if the compiler generating the shared library uses another encoding scheme for the function names as the compiler that is to use the dll. As that also applies to class-constructors you need to export means to get pointers to the dll-interface-class with the extern "C" construct. The dll-interface-class can then be used to construct instances of the other classes to be exported. (Of course, functions to construct instances could be exported just as the accessor function for the dll-interface-class but that way you'd have more trouble when using the dll - we'll come to that later).
__ dllinterface.h __
/* First a typedef that can be used by the importers */
typedef class ISomeDll* (*dll_interface_function_t)();
/* And the function they should request - Note that it does not need to 
 * get declared in the header if the function is to be requested by
 * GetProcAddress/dlsym
 *
extern "C" __declspec(dllexport) class ISomeDll* getSomeHandle();
*/
Note that, to avoid confusion, getSomeHandle is best defined in the global namespace: C-linkage does not know about namespaces. Then in a cpp-source file you can simply define the implementation of the dll-interface:
__ dllinterface.cpp __
#include "dllinterface.h"
#include "exported_class.h"

/* Make sure the compiler does not glitter the symbol with type-informations and that
   it gets exported. */
extern "C" __declspec(dllexport) class ISomeDll* getSomeHandle();

class ISomeDll* getSomeHandle()
{
  /* The implementation can be defined locally. */
  static class CSomeDll : public ISomeDll
  {
  public:
  ISomeExportedClass* anExportedFunction() const { return new CSomeExportedClass(); }
  } myInterface;

return &myInterface;
};

Importing the library

Now it is fairly easy to get access to the exported funtions. Simply include the header defining ISomeDll and getSomeHandle() and do something like this:
#include "dllinterface.h"

  HMODULE dllHandle = LoadLibrary("somedll.dll");
  /* Error handling... */
  dll_interface_function_t getDllInterface = (dll_interface_function_t) GetProcAddress(dllHandle, "getSomeHandle");
  /* Error handling
     if(getDllInterface == 0) ....
  */
Then you can access the exported functions by calling functions from the dll-interface
/* Construct an instance */
ISomeExportedClass* imported = getDllInterface()->anExportedFunction();
/* Call a method - of course an error check should be done first... */
int result = imported->aMethod();
Using this mechanism you do not need any import-libraries but only the dll and the headers defining the exported interfaces/functions. Note that the methods of interface-classes exposed by the library should not return strings by using std::string but rather const char* instead, else you will run into trouble if a client-compiler-system uses a different STL-implementation. This also applies to other STL-classes.

Deleting objects

Although the destructor(s) of classes are exported through the library interface one should not rely on that they work correctly when called by an importing library. What happens when creating and deleting an object of a given class is basically
    /* Allocation of memory */
    /* Calling the class' constructor-code */
    /* ... */
    /* Calling the the class' destructor-code */
    /* Freeing the memory */
This means that when an object gets allocated in a module this involves using the runtime-library that is used by that module to allocate the memory for the object. When trying to delete the object in another module this may cause problems because the runtime-library linked with the deleting module may be another. This is also a problem when passing a buffer allocated in one module to another where it is to be deleted after use. The only way around this is to export the memory-functions to any modules that shall be able to free buffers allocated for them.
    void EXPORT myModuleFree(void* aPointer) { free(aPointer); }
For c++-classes this requires to export a deleting-function for each object-class to be deleteable by other modules.


Back to home
Copyright (C) 2012 Heiko Lewin