Tutorials

This tutorial will guide you through developing cowtalk, a plugin based program designed to use dynamic plugins to produce bovine utterances. This tutorial assumes you are using the autotools suite (autoconf, automake and libtool). If you are not, you really ought to, as it dramatically eases the development cycle and improves the portability of your software package.

Step to integrate plump into your project

  • create your new project directory
      • mkdir cowtalk
      • cd cowtalk
    • extract plump sources into your project directory
      • cp /path/to/plump/package/plump-0.0.1.tar.gz .
      • tar zxvf plump-0.0.1.tar.gz
    • edit pluginInterface.h to suit your plugin requirements. In this case, our plugins will need to expose only one member method in their interface. Add the following public method:
      • virtual void utter (std::string) = 0;
    • implement your own plugins. Create cowsayPlugin.h and cowthinkPlugin.h. Classes CowsayPlugin and CowthinkPlugin should implement PluginInterface interface. This means CowsayPlugin and CowthinkPlugin should publicly inherit from PluginInterface and provide an implementation for each pure virtual method defined in PluginInterface. Additionally, each plugin need to supply a `create' and `destroy' extern "C" functions that new and delete an instance of the plugin class they implement.
    // cowsayPlugin.h
    #include "plump-0.0.1/src/plump.h"
    
    #include 
    #include 
    
    class CowsayPlugin : public plump::PluginInterface {
      public:
        CowsayPlugin () {};
        ~CowsayPlugin () {};
    
        virtual void utter (std::string);
    
      private:
    
    };
    
    extern "C" plump::PluginInterface* create();
    extern "C" void destroy( plump::PluginInterface* );
    
    // cowsayPlugin.cxx
    #include "cowsayPlugin.h"
    
    void CowsayPlugin::utter (std::string str)
    {
        std::cout << " " << str << std::endl;
        std::cout << " ------                      " << std::endl;
        std::cout << "        \\   ^__^             " << std::endl;
        std::cout << "         \\  (oo)\\_______     " << std::endl;
        std::cout << "            (__)\\       )\\/\\ " << std::endl;
        std::cout << "                ||----w |    " << std::endl;
        std::cout << "                ||     ||    " << std::endl;
    }
    
    extern "C" plump::PluginInterface* create()
    {
        return new CowsayPlugin();
    }
    
    extern "C" void destroy( plump::PluginInterface* const pluginPtr )
    {
        delete pluginPtr;
    }
    
    // cowthinkPlugin.h
    #include "plump-0.0.1/src/plump.h"
    
    #include 
    #include 
    
    class CowthinkPlugin : public plump::PluginInterface {
      public:
        CowthinkPlugin () {};
        ~CowthinkPlugin () {};
    
        virtual void utter (std::string);
    
      private:
    
    };
    
    extern "C" plump::PluginInterface* create();
    extern "C" void destroy( plump::PluginInterface* );
    
    // cowthinkPlugin.cxx
    #include "cowthinkPlugin.h"
    
    void CowthinkPlugin::utter (std::string str)
    {
        std::cout << " " << str << std::endl;
        std::cout << " ~~~~~~                      " << std::endl;
        std::cout << "        o   ^__^             " << std::endl;
        std::cout << "         o  (oo)\\_______     " << std::endl;
        std::cout << "            (__)\\       )\\/\\ " << std::endl;
        std::cout << "                ||----w |    " << std::endl;
        std::cout << "                ||     ||    " << std::endl;
    }
    
    extern "C" plump::PluginInterface* create()
    {
        return new CowthinkPlugin();
    }
    
    extern "C" void destroy( plump::PluginInterface* const pluginPtr )
    {
        delete pluginPtr;
    }
    
    • create cowtalk.cxx
    // cowtalk.cxx
    #include "plump-0.0.1/src/plump.h"
    
    void callback_utter (plump::PluginInstance* plugin, void* data)
    {
        std::string* str = reinterpret_cast (data);
        plugin->instance ()->utter (*str);
    }
    
    int main (int argc, char** argv)
    {
        std::string data = "foobar"; 
    
        plump::Plump plump;
        plump.setPath (".");
        plump.discoverPlugins ();
        plump.registerCallback (&callback_utter, &data);
        plump.run ();
    
        return 0;
    }
    
    • create configure.ac
    dnl Process this file with autoconf to produce a configure script.
    AC_INIT([cowtalk], [0.0.1], [matt_vescovi@yahoo.co.uk])
    AM_INIT_AUTOMAKE
    
    AC_CONFIG_SRCDIR([cowtalk.cxx])
    
    dnl Checks for programs.
    AC_PROG_INSTALL
    AC_PROG_CC
    AC_PROG_CPP
    AC_PROG_CXX
    AC_PROG_CXXCPP
    
    dnl Checks for libtool
    AC_PROG_LIBTOOL
    
    dnl Checks for libraries.
    
    dnl Checks for header files.
    AC_STDC_HEADERS
    
    dnl Configure plump
    AC_CONFIG_SUBDIRS(plump-0.0.1)
    
    dnl Init CPPUnit framework
    #AM_PATH_CPPUNIT(1.9.6)
    
    dnl Generate Makefiles
    AC_OUTPUT([Makefile])
    
    • create Makefile.am
      • build example executable linking against libplump.la
      • build plugins linking against libplugin.la
    SUBDIRS =                plump-0.0.1
    
    bin_PROGRAMS =                cowtalk
    lib_LTLIBRARIES =        cowsayPlugin.la cowthinkPlugin.la
    
    cowtalk_SOURCES =        cowtalk.cxx
    cowtalk_LDADD =                $(top_srcdir)/plump-0.0.1/src/libplump.la
    cowtalk_CPPFLAGS =        $(LTDLINCL)
    
    cowsayPlugin_la_SOURCES =        cowsayPlugin.h cowsayPlugin.cxx
    cowsayPlugin_la_LIBADD =        $(top_srcdir)/plump-0.0.1/src/libplugin.la
    cowsayPlugin_la_LDFLAGS =        -module -avoid-version -no-undefined
    
    cowthinkPlugin_la_SOURCES =        cowthinkPlugin.h cowthinkPlugin.cxx
    cowthinkPlugin_la_LIBADD =        $(top_srcdir)/plump-0.0.1/src/libplugin.la
    cowthinkPlugin_la_LDFLAGS =        -module -avoid-version -no-undefined
    
    • autoconfiscate your new project
      • aclocal
      • autoconf
      • autoheader
      • libtoolize --force --copy
      • touch AUTHORS NEWS ChangeLog
      • automake -a
      • ./configure
    • build it
      • make
    • run it
      • ./cowtalk
    • now that the project has been built, let us add a new plugin (cowshout) and dynamically add it to our project. Creation and integration of a new plugin into our cowtalk system does not require any existing components of the system to be rebuilt. Only the new plugin to be added needs to be built. The existing executables (cowtalk) and libraries (libplugin, libplump) need not be rebuilt.
    // cowshoutPlugin.h
    #include "plump-0.0.1/src/plump.h"
    
    #include 
    #include 
    #include 
    
    class CowshoutPlugin : public plump::PluginInterface {
    public:
        CowshoutPlugin () {};
        ~CowshoutPlugin () {};
    
        virtual void utter (std::string);
    
    private:
        void toupper (std::string&);
    
    };
    
    extern "C" plump::PluginInterface* create();
    extern "C" void destroy( plump::PluginInterface* );
    
    // cowshoutPlugin.cxx
    #include "cowshoutPlugin.h"
    
    void CowshoutPlugin::utter (std::string str)
    {
        toupper (str);
        
        std::cout << " " << str << "!" << std::endl;
        std::cout << " ------                      " << std::endl;
        std::cout << "        \\\\   ^__^             " << std::endl;
        std::cout << "         \\\\  (oo)\\_______     " << std::endl;
        std::cout << "            (__)\\       )\\/\\ " << std::endl;
        std::cout << "                ||----w |    " << std::endl;
        std::cout << "                ||     ||    " << std::endl;
    }
    
    void CowshoutPlugin::toupper (std::string& str)
    {
        for (std::string::iterator it = str.begin();
            it != str.end();
            it++ )
        {
            *it = ::toupper (*it);
        }
    }
    
    extern "C" plump::PluginInterface* create()
    {
        return new CowshoutPlugin();
    }
    
    extern "C" void destroy( plump::PluginInterface* const pluginPtr )
    {
        delete pluginPtr;
    }
    
    • build the new plugin by adding `cowshoutPlugin.la' to the
    lib_LTLIBRARIES and the following lines to Makefile.am
    cowshoutPlugin_la_SOURCES =        cowshoutPlugin.h cowshoutPlugin.cxx
    cowshoutPlugin_la_LIBADD =        $(top_srcdir)/plump-0.0.1/src/libplugin.la
    cowshoutPlugin_la_LDFLAGS =        -module -avoid-version -no-undefined
    
    and running make again.
      • make
    • run the executable again and observe the output of the new plugin.
      • ./cowtalk
    • one of plump's most useful features is the ability to create, manage and run multiple plugins instantiated from the same plugin module. This embodies the concept that a plugin module and a plugin instance are different entities. Hence, one can create multiple instances of one plugin from the plugin module and plump will transparently handle the underlying complexity. Modify cowtalk.cxx to explicitly use the plugins required: the following example creates two instances of `cowsay' and `cowthink' plugins and one instance of `cowshout' plugin.
    // cowtalk.cxx
    #include "plump-0.0.1/src/plump.h"
    
    void callback_utter (plump::PluginInstance* plugin, void* data)
    {
        std::string* str = reinterpret_cast (data);
        plugin->instance ()->utter (*str);
    }
    
    int main (int argc, char** argv)
    {
        std::vector plugins;
        plugins.push_back ("cowsayPlugin");
        plugins.push_back ("cowthinkPlugin");
        plugins.push_back ("cowsayShout");
        plugins.push_back ("cowthinkPlugin");
        plugins.push_back ("cowsayPlugin");
    
        std::string data = "foobar"; 
    
        //    plump::Logger::getLogger()->setLevel (Logger::DEBUG);
    
        plump::Plump plump;
        plump.setPath (".");
        plump.usePlugins (plugins);
        plump.registerCallback (&callback_utter, &data);
        plump.run ();
    
        return 0;
    }
    
    • plugins are not limited to operate on the same data. Each plugin can manipulate its own individual resources. This is accomplished at plugin creation time and requires the user to define and implement a plugin constructor which takes an additional arbitrary parameter, normally representing the plugin's individual resources or initialization data.