Sunday 31 March 2013

C++ signal handler example

Here is a C++ class implementation to handle signals on unix and linux. A common usage of signal handlers is to allow an application to close down properly (close database connections, write buffers to files etc) when a kill signal is received it can be useful to include a handler for SIGINT and SIGTERM.

Class definition SigHandler.h :
class SigHandler
{
public:
    SigHandler();
    ~SigHandler();
    
    bool setSignalToHandle(int sig);
    static bool isSignalSet();    
    static void setSignal(int unused);
    
private:
    static bool m_signalSet;
    
};

Implenentation SigHandler.cpp:
#include "SigHandler.h"
#include "signal.h"

using namespace std;

bool SigHandler::m_signalSet = false;

SigHandler::SigHandler()
{
}

SigHandler::~SigHandler()
{
}

bool SigHandler::setSignalToHandle(int sig)
{
    if(signal(sig, SigHandler::setSignal) == SIG_ERR) {
        return false;
    }
    return true;
}

void SigHandler::setSignal(int unused)
{
    m_signalSet = true;
}

bool CxmlSigHandler::isSignalSet()
{
    return m_signalSet;
}

A method is required to handle the signal and must return void and take an int parameter.

static void setSignal(int unused);

This is registered with the signal function from signal.h and is invoked whenever the signal is triggered.
bool SigHandler::setSignalToHandle(int sig)
{
    if(signal(sig, SigHandler::setSignal) == SIG_ERR) {
        return false;
    }
    return true;
}

Whenever the signal "sig" is encountered this method is called and will set our flag to true.

Below is a simple example which will loop until a SIGTERM is encountered (generated by kill command):

SigHandler      sigHandler;
sigHandler.setSignalToHandle(SIGTERM); //register SIGTERM to be handled

while(!sigHandler.isSignalSet() { //loop until signal received
    //do stuff here
}

For reference: signal.h

Tuesday 26 March 2013

C++ plugin loading from shared object library

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";
}