Here is a very simple C++ plugin loading class for unix/linux platforms. It is essentially a wrapper for the dlopen/dlsym functions from
dlfcn.h. The objective here is to retrieve a known entry point within the library object to construct a plugin object. The application containing the plugin loader does not need to know the contents of the library, just how to access a specific plugin when required. The benefit of this set up is that the application and the plugin modules are totally decoupled, plugins can be added without the application needing to be re-linked.
The class PluginLoader is constructed with the path to a shared object library containing the actual plugin(s):
PluginLoader loader("[path to object library]");
The template function
getSymbol (string symbol, T &value)
does the work of retrieving the entry point from the library. The idea is to pass a function pointer type which can be used to construct the plugin. This is preferable to returning a pointer to a created object as the application has full control over construction/destruction.
In the simple example below a plugin base class is created from which two plugins (PluginA and PluginB) are derived and must implement the pure virtual
outputMessage();
The functions to construct the objects are declared "extern C" as this prevents C++
name mangling.
extern "C" PluginBase* LOAD_PLUGIN_A() {
return new PluginA();
}
Below is a simple example which extracts two plugins, constructs them and runs the outputMessage() method:
typedef PluginBase* (*plugin_base_initfunc)(); //typedef to function pointer
int main(int argc, char** argv) {
try
{
PluginLoader loader("libSimplePlugin.so");
PluginBase* pPluginA = 0;
PluginBase* pPluginB = 0;
plugin_base_initfunc func;
loader.getSymbolFunc("LOAD_PLUGIN_A", func); //extract function to construct a PluginA object
pPluginA = (func)(); //construct plugin object
cout << pPluginA->outputMessage() << endl;
delete pPluginA;
loader.getSymbolFunc("LOAD_PLUGIN_B", func); //extract function to construct a PluginB object
pPluginB = (func)(); //construct plugin object
cout << pPluginB->outputMessage() << endl;
}
catch (exception &e) {
cout << e.what() << endl;
}
return 0;
}
This produces the following output:
./pluginapp
PluginA: Hello world
PluginB: Hello world
See full source files below:
Class definition for PluginLoader.h
/**
* Description: Class definition for loading shared object libraries at runtime.
*/
#ifndef PLUGIN_LOADER_H
#define PLUGIN_LOADER_H
#include "string.h"
#include "iostream"
using namespace std;
class PluginLoader
{
public:
/**
*
* @param libname path to required shared library file
*/
PluginLoader(string libname);
virtual ~PluginLoader();
/**
*
* @param symbol symbol (defined using extern "C") to extract from opened shared library. *
* @return address of symbol (either function or object)
*/
void *getSymbol(string symbol) const;
/**
*
* @return name of shared library
*/
string getLibName() const {
return(m_libname);
};
template void getSymbol (string symbol, T &value) const {
void *sym = getSymbol(symbol);
value = (T)(sym);
}
private:
void *m_lib;
string m_libname;
};
#endif
Implementaion PluginLoader.cpp:
#include "CpluginLoader.h"
#include "sstream"
#include "dlfcn.h"
#include "iostream"
#include "stdexcept"
using namespace std;
PluginLoader::PluginLoader(string libname) {
m_libname = libname;
m_lib = dlopen(m_libname.c_str(), RTLD_LAZY | RTLD_GLOBAL);
if (m_lib == 0) {
stringstream errortext;
if (char * err = dlerror()) {
errortext << " dlopen() error: " << err << endl;
throw runtime_error(errortext.str());
}
}
}
PluginLoader::~PluginLoader() {
dlclose(m_lib);
}
void *
PluginLoader::getSymbol(string symbol) const {
void *sym = 0;
if (m_lib) {
sym = dlsym(m_lib, symbol.c_str());
if (char * err = dlerror()) {
stringstream errortext;
errortext << "DynamicLibrary::getSymbol() - Error on " << symbol << "message: " << err;
throw runtime_error(errortext.str());
}
} else {
throw runtime_error("DynamicLibrary::getSymbol() - Error accessing library");
}
return (sym);
}
Class definition PluginBase:
class PluginBase
{
public:
PluginBase();
virtual ~PluginBase();
virtual string outputMessage()=0;
};
Class definition PluginA:
class PluginA : public PluginBase
{
public:
PluginA();
virtual ~PluginA();
virtual string outputMessage();
};
extern "C" PluginBase* LOAD_PLUGIN_A() {
return new PluginA();
}
Implementaion of pure virtual:
string PluginA::outputMessage()
{
return "PluginA: Hello world";
}