Skip to content

Instantly share code, notes, and snippets.

@jpclipffel
Last active November 8, 2017 16:51
Show Gist options
  • Save jpclipffel/1d23f6ad338877a0fdf3 to your computer and use it in GitHub Desktop.
Save jpclipffel/1d23f6ad338877a0fdf3 to your computer and use it in GitHub Desktop.
C++ module and module loader example
Example of an C++ module (== dynamic library which exports an object).
* cpp_imod.hh: Module interface;
* cpp_module.hh: Module implementation header;
* cpp_module.cpp: Module implementation source;
* cpp_modloader-dlfcn.cpp: Load and call module.
/**
* C++ module example - module interface
* Define and implement module interface.
*/
#ifndef IMOD_HH
#define IMOD_HH
/**
* Module interface.
* All modules must inherit from this interface.
* Rules:
* - must implement constructor
* - must implement destructor
* - other methods must be virtual pure
*/
struct imod
{
imod(){};
virtual ~imod(){};
virtual void foo() = 0;
};
#endif
/**
* C++ module example - loader implementation
* Load a test module.
*/
#include <iostream>
#include <dlfcn.h>
#include "cpp_imod.hh"
using namespace std;
/**
* Load a module (an object from a .so file) and call a method from
* this module.
* Compilation: g++ <cpp-modloader-dlfcn.cpp> -ldl -o cpp-modloader-dlfcn
*/
int main(int ac, char **av)
{
void *libfd; // Library handler
imod *module; // Module instamce (pointer)
imod *(*builder)(); // Module factory
void (*destroyer)(imod*); // Module destructor function
// Test arguments
if (ac < 2)
{
cerr << "usage: modloader <module.so>" << endl;
return 1;
}
// Clear all previous errors
dlerror();
// Open library
libfd = dlopen(av[1], RTLD_LAZY);
// Library pointer is NULL: error
if (!libfd)
{
// dlerror() return last error string
cerr << "dlopen(): " << dlerror() << endl;
return 1;
}
/**
* Get module factory symbol.
*
* dlsym() can return NULL (see man); however, in our case, the builder
* function should not be NULL.
* The correct (generic) way to find if error happens is to:
* - call dlerror()
* - call dlsym()
* - call dlerror() once again, store the result and check if it contains
* an error.
*/
builder = (imod *(*)())dlsym(libfd, "build");
if (!builder)
{
cerr << "dlsym(): " << dlerror() << endl;
dlclose(libfd);
return 1;
}
/**
* Get module destructor symbol.
* Same remark as before.
*/
destroyer = (void (*)(imod*))dlsym(libfd, "destroy");
if (!destroyer)
{
cerr << "dlsym(): " << dlerror() << endl;
dlclose(libfd);
return 1;
}
// Create a new module instance
module = builder();
// Check if module have been created
if (!module)
{
cerr << "builder(): return NULL instead of a new module" << endl;
dlclose(libfd);
return 1;
}
// Call module method(s)
module->foo();
// Delete module
destroyer(module);
// Don't forget to close the library
dlclose(libfd);
return 0;
}
/**
* C++ module example - module implementation
* Implement test module.
*/
#include "cpp_module.hh"
module::module()
{
}
module::~module()
{
cout << "module::~module()" << endl;
}
void module::foo()
{
cout << "module::foo()" << endl;
}
/**
* C++ module example - module header
* Define module header.
*/
#ifndef MODULE_HH
#define MODULE_HH
#include <iostream>
#include "cpp_imod.hh"
using namespace std;
/**
* Module (implementation) header.
* It MUST:
* - inherit from interface (imod.hh)
* - defines the constructor and destructor
* - defines interface methods as virtual
*/
struct module: public imod
{
module();
~module();
virtual void foo();
};
/**
* Module factory.
* Since dlsym() except C mangled functions, we need to specify our
* factory mangling.
*/
extern "C" imod *build()
{
return new module;
}
/**
* Module destructor.
* Same remark as before.
*/
extern "C" void destroy(imod *_self)
{
if (_self)
delete _self;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment