Exporting C++ classes from a DLL

September 16th, 2011 at 8:49 am

Because of ABI incompatibilities between compilers and even different versions of the same compiler, exporting C++ classes from DLLs is a tricky business. Luckily, with some care it is possible to do this safely, by employing abstract interfaces.

In this post I will show a code sample of a DLL and an application using it. The DLL exports a class by means of a factory function that creates new objects that adhere to a known abstract interface. The main application loads this DLL explicitly (with LoadLibrary) and uses the objects created by it. The code shown here is Windows-specific, but the same method should work for Linux and other platforms. Also, the same export technique will work for implicit DLL loading as well.

First, we define an abstract interface (by means of a class with pure virtual methods, and no data), in a file named generic_interface.h:

class IKlass {
public:
    virtual void destroy() = 0;
    virtual int do_stuff(int param) = 0;
    virtual void do_something_else(double f) = 0;
};

Note that this interface has an explicit destroy method, for reasons I will explain later. Now, the DLL code, contained in a single C++ file:

#include "generic_interface.h"
#include <iostream>
#include <windows.h>

using namespace std;


class MyKlass : public IKlass {
public:
    MyKlass()
        : m_data(0)
    {
        cerr << "MyKlass constructor\n";
    }

    ~MyKlass()
    {
        cerr << "MyKlass destructor\n";
    }

    void destroy()
    {
        delete this;
    }

    int do_stuff(int param)
    {
        m_data += param;
        return m_data;
    }

    void do_something_else(double f)
    {
        int intpart = static_cast<int>(f);
        m_data += intpart;
    }
private:
    int m_data;
};

extern "C" __declspec(dllexport) IKlass* __cdecl create_klass()
{
    return new MyKlass;
}

There are two interesting entities here:

  1. MyKlass – a simplistic implementation of the IKlass interface.
  2. A factory function for creating new instances of MyKlass.

And here is a simple application (also contained in a single C++ file) that uses this library by loading the DLL explicitly, creating a new object and doing some work with it:

#include "generic_interface.h"
#include <iostream>
#include <windows.h>

using namespace std;

// A factory of IKlass-implementing objects looks thus
typedef IKlass* (__cdecl *iklass_factory)();


int main()
{
    // Load the DLL
    HINSTANCE dll_handle = ::LoadLibrary(TEXT("mylib.dll"));
    if (!dll_handle) {
        cerr << "Unable to load DLL!\n";
        return 1;
    }

    // Get the function from the DLL
    iklass_factory factory_func = reinterpret_cast<iklass_factory>(
        ::GetProcAddress(dll_handle, "create_klass"));
    if (!factory_func) {
        cerr << "Unable to load create_klass from DLL!\n";
        ::FreeLibrary(dll_handle);
        return 1;
    }

    // Ask the factory for a new object implementing the IKlass
    // interface
    IKlass* instance = factory_func();

    // Play with the object
    int t = instance->do_stuff(5);
    cout << "t = " << t << endl;
    instance->do_something_else(100.3);
    int t2 = instance->do_stuff(0);
    cout << "t2 = " << t2 << endl;

    // Destroy it explicitly
    instance->destroy();
    ::FreeLibrary(dll_handle);

    return 0;
}

Alright, I raced through the code, but there are a lot of interesting details hiding in it. Let’s go through them one by one.

Clean separation

There are other methods of exporting C++ classes from DLLs (here’s one good discussion of the subject). The one presented here is the cleanest – the least amount of information is shared between the DLL and the application using it – just the generic interface header defining IKlass and an implicit agreement about the signature of the factory function.

The actual MyKlass can now use whatever it wants to implement its functionality, without exposing any additional details to the application.

Additionally, this code can easily serve as a basis for an even more generic plugin architecture. DLL files can be auto-discoverable from a known location, and a known function can be exposed from each that defines the exported factories.

Memory management

Memory management between DLLs can be a real pain, especially if each DLL links the MSVC C runtime statically (which tends to be common on Windows). Memory allocated in one DLL must not be released in another in such cases.

The solution presented here neatly overcomes this issue by leaving all memory management to the DLL. This is done by providing an explicit destroy function in the interface, that must be called when the object is no longer needed. Naturally, the application can wrap these objects by a smart pointer of some kind to implement RAII.

Note that destroy is implemented with delete this. This may raise an eyebrow or two, but it’s actually valid C++ that occasionally makes sense if used judiciously.

It’s time for a pop quiz: why doesn’t IKlass need a virtual destructor?

Name mangling and calling convention

You’ve surely noticed that the signature of create_klass is rather intricate:

extern "C" __declspec(dllexport) IKlass* __cdecl create_klass()

Let’s see what each part means, in order:

  • extern "C"tells the C++ compiler that the linker should use the C calling convention and name mangling for this function. The name itself is exported from the DLL unmangled (create_klass)
  • __declspec(dllexport) – tells the linker to export the create_klass symbol from the DLL. Alternatively, the name create_klass can be placed in a .def file given to the linker.
  • __cdecl – repeats that the C calling convention is to be used. It’s not strictly necessary here, but I include it for completeness (in the typedef for iklass_factory in the application code as well).

There is a variation on this theme, which I’ll mention because it’s a common problem people run into.

One can declare the function with the __stdcall calling convention instead of __cdecl. What this will do is cause GetProcAddress to not find the function in the DLL. A peek inside the DLL (with dumpbin /exports or another tool) reveals why – __stdcall causes the name to be mangled to something like _create_klass@0. To overcome this, either place the plain name create_klass in the exports section of the linker .def file, or use the full, mangled name in GetProcAddress. The latter might be required if you don’t actually control the source code for the DLL.

Related posts:

  1. Compiling C DLLs and using them from Perl
  2. Python objects, types, classes, and instances – a glossary
  3. Equivalence classes and group partitions
  4. Finding out where a function was called from
  5. coding a simplified cpp

5 Responses to “Exporting C++ classes from a DLL”

  1. Arnaud DiederenNo Gravatar Says:

    Here’s something I don’t quite understand: If the goal here is to bypass all compilers ABI differences, how can we be sure that, in the main program, when calling do_stuff(), the call be at the right location?

    * Since those functions are virtual, they reside in a vtable
    * vtables are stored somewhere in the allocated memory for the class instance (at offset 0, I think?)
    * vtables consist of pointers to function implementations

    But, how can we be sure that the format of the vtables of different compilers are compatible?

    If the “MyKlass.cpp” plugin was compiled with, say, Foo++Compiler 1.2, the layout of the vtable could be something like:

    ————————————-
    0×0: dword ptr -> do_stuff
    ————————————-
    0×4: dword ptr -> do_something_else
    ————————————-

    But then, the main program, that loads the plugin, and that was compiled wyth Bar++Compiler 8.1, thinks the layout of IKlass vtables is:

    ————————————-
    0×0: dword ptr -> RTTI information (or whatever metadata)
    ————————————-
    0×4: dword ptr -> do_stuff
    ————————————-
    0×8: dword ptr -> do_something_else
    ————————————-

    Then, from the main program, when calling “do_stuff()”, what is really performed is a:
    “call vtable_ptr+4″, which will actually call “MyKlass::do_something_else()”.

    …or at least, that’s the way it is in my mind :D

    Am I missing something?

  2. elibenNo Gravatar Says:

    Arnaud,

    This is a perfectly valid concern. However, it turns out that almost all compilers implement the same vtable layout in memory. This is done in order to be compatible with COM. There are some details in this article, and more can be found online.

  3. Arnaud DiederenNo Gravatar Says:

    Eli, thanks for the clarifications!
    A>

  4. ملف dllNo Gravatar Says:

    thanks much for your post

    i will bookmark it to read it later :)

  5. Michele AlessandriniNo Gravatar Says:

    About memory management, every time I think at this, I’m afraid it’s not enough to have the class created end destroyed in the DLL. What if a member of the class returns an object? This is very common, think for example a string or any other object. When the object is created (in the DLL) its constructor can allocate memory, that’s then freed by the main program, so this is again a problem, am I wrong?
    I think the onlty safe solution is assuming everybody is using the same runtime, and so you can avoid the destroy() function.

Leave a Reply

To post code with preserved formatting, enclose it in `backticks` (even multiple lines)