Monday, July 30, 2012

How to Dynamically Load C++ Shared Libraries on Linux

Below is an example how to dynamically load a C++ shared library on Linux. This is the project structure.
|-- include
|   |-- Hello.h
|   `-- IHello.h
|-- src
|   |-- Hello.cpp
|   `-- Main.cpp
`-- wscript
IHello.h
#ifndef IHELLO_H_
#define IHELLO_H_

class IHello {
public:
    virtual void SayHello() const = 0;
    virtual ~IHello() {};
};

typedef IHello* (*CreateFunc)();
typedef void (*DestroyFunc)(IHello*);

#endif /* IHELLO_H_ */
First, we need to create C-like functions for creating an instance of Hello and destroying it. This is a kind of factory class. Second, we need to add extern "C" to avoid C++ name mangling.
Hello.h
#ifndef HELLO_H_
#define HELLO_H_

#include "IHello.h"

class Hello : public IHello {
public:
    Hello();
    void SayHello() const;
    virtual ~Hello();
};

#endif /* HELLO_H_ */
Hello.cpp
#include <iostream>
#include "Hello.h"
using namespace std;

Hello::Hello() {
    cout << "Creating Hello" << endl;
}

Hello::~Hello() {
    cout << "Destroying Hello" << endl;
}

void Hello::SayHello() const {
    cout << "Hello World" << endl;
}

extern "C" IHello* CreateHello() {
    return new Hello;
}

extern "C" void DestroyHello(IHello* h) {
    delete h;
}
Main.cpp
#include <iostream>
#include <dlfcn.h>
#include "IHello.h"
using namespace std;

int main() {
    void* helloLib = dlopen("./libhello.so", RTLD_LAZY);
    if (!helloLib) {
        cerr << "Error loading libhello.so" << endl;
        return 1;
    }

    CreateFunc cf = (CreateFunc) dlsym(helloLib, "CreateHello");
    IHello* hello = cf();
    hello->SayHello();

    DestroyFunc df = (DestroyFunc) dlsym(helloLib, "DestroyHello");
    (df)(hello);

    if (dlclose(helloLib)) {
        cerr << "Error closing libhello.so" << endl;
        return 1;
    }

    return 0;
}
As you can see in the Main.cpp, we only include IHello.h and not Hello.h (the implementation), which is a pure virtual class. This is something like an interface in Java/C#.
wscript
#!/usr/bin/env python

top = '.'
out = 'build'

def options(opt):
    opt.load('compiler_cxx')
    
def configure(ctx):
    ctx.load('compiler_cxx')
    
def build(ctx):
    ctx.shlib(source='src/Hello.cpp', cxxflags=['-g', '-Wall'],
              target='hello', includes=['include'])
    
    ctx.program(source='src/Main.cpp', cxxflags=['-g', '-Wall'],
                lib=['dl'], includes=['include'], target='main')

Let's build it now.
waf configure build
cd build
./main
Below is the output.
Creating Hello
Hello World
Destroying Hello

No comments:

Post a Comment