Menu

Tutorial

Alex

THIS PAGE IS OBSOLETE.
Use the [Animal Tutorial].

All source code for the tutorial comes with the distribution of Pugg.
This tutorial is written for Pugg vs0.3 and should work for above versions without major changes.

General Idea

The general idea behind Pugg is to write base classes in your application, and load user written sub-classes from dll files. Pugg provides a framework to ease and automate loading and usage of these sub-classes.

Writing Base classes

Let’s say we have two different classes, Shoe and Hat. These classes have one virtual function called getName, which returns a std::string defining the class itself.

#!C++
    class Shoe  
    {  
       public:  
       virtual std::string getName() {return "regular shoe";}  
    };  

    class Hat  
    {  
    public:  
       virtual std::string getName() {return "regular hat";}  
    };  

Writing Base Class Drivers

Pugg requires another driver class to be defined for every base class. These drivers are abstract classes derived from pugg::Driver. User written plug-ins override these driver classes to make a connection with the base class.
Note: pugg::Driver is defined in pugg/Driver.h

#!C++
    class ShoeDriver : public pugg::Driver  
    {  
    public:  
       virtual Shoe* createShoe() = 0;  
    };  

    class HatDriver : public pugg::Driver  
    {  
    public:  
       virtual Hat* createHat() = 0;  
    };  

The createHat and createShoe functions allow users to return an instance of their sub-classes. These createXXX functions are just a rewrite of the base class constructors. If our base class had a constructor accepting parameters, we would have a createXXX function accepting related types of parameters.

eg.

#!C++
    class foo  
    {  
    public:  
       foo(int i) ();  
    //  class continues  
    };  
    class fooDriver : public pugg:Driver  
    {  
    public:  
       virtual foo* createFoo(int i) = 0;  
    };  

Server and Version definitions

A server is a controller class for drivers. Every driver type has a connected server. These servers are stored in a kernel class. For identification purposes every server has a name. Our first definition is the name of the server.

#!C++
    #define SHOE_SERVER_NAME "ShoeServer"  
    #define HAT_SERVER_NAME "HatServer"  

Our second definition is the version of our base class. Pugg controls the versions of the base class and loaded plug-ins and prevents out-dated plug-ins from loading.

#!C++
    #define SHOE_CLASS_VERSION 1  
    #define HAT_CLASS_VERSION 1  

Writing Plug-ins

Our plug-in will hold a subclass of shoe and a subclass of hat.

#!C++
    class BlueShoe : public Shoe  
    {  
    public:  
       std::string getName() {return "Blue shoe";}  
    };  

    class BlueHat : public Hat  
    {  
    public:  
       std::string getName() {return "Blue hat";}  
    };  

As mentioned earlier the whole idea behind Pugg is to match drivers. We will write drivers for our sub-classes.

#!C++
    class myShoeDriver : public ShoeDriver  
    {  
       std::string getName() {return "blueshoe";}  
       Shoe* createShoe() {return new BlueShoe();}  
    };  

    class myHatDriver : public HatDriver  
    {  
       std::string getName() {return "bluehat";}  
       Hat* createHat() {return new BlueHat();}  
    };  

The getName function returns a std::string name, to be associated with the sub-class. Pugg holds drivers in maps with std::string keys for easy access.

CreateXXX function basically creates an instance of the sub-class, like a constructor.

Dll Function for Auto Loading

Every plug-in needs to have a registerPlugin function defined to be recognized by Pugg. This function registers drivers of the sub-classes to Pugg system.

~~~~~~~~~~~~~~~~~~~

!C++

#include "BlueHat.h"  
#include "BlueShoe.h"

using namespace pugg;

extern "C" __declspec(dllexport) void registerPlugin(Kernel &K)  
{  
   Server* server = CastToServerType(K.getServer(SHOE_SERVER_NAME));  
   if (server != NULL)  
   {  
      server->addDriver(new myShoeDriver(),SHOE_CLASS_VERSION);  
   }

   Server* server2 = CastToServerType(K.getServer(HAT_SERVER_NAME));  
   if (server2 != NULL)  
   {  
      server2->addDriver(new myHatDriver(),HAT_CLASS_VERSION);  
   }  
}

~~~~~~~~~~~~~~~~~~

For every driver type we gather the related server, then add our drivers. First we get the server from kernel using the server name defined earlier. Then we use the CastToServerType function to cast the server class into sub-class that we can with our driver. Then we use the addDriver function of server to add our drivers to the related server.

Loading the Plug-ins

First we create servers for every base class.

#!C++
    pugg::Server<ShoeDriver>* shoeServer = new pugg::Server(SHOE_SERVER_NAME,SHOE_CLASS_VERSION);  
    pugg::Server<HatDriver>* hatServer = new pugg::Server(HAT_SERVER_NAME,HAT_CLASS_VERSION);  

As you can see, servers are template definitions. The template parameter is the driver class related to the base class.

The constructer parameters are our engine name and engine version which we defined before using the preprocessor (#defines).

#!C++
    pugg::Kernel myKernel;  
    myKernel.addServer(shoeServer);  
    myKernel.addServer(hatServer);

For server management aPugg provides a class named pugg::Kernel. We define a Kernel object and add our servers to it.

Here we have some scary code, but don’t be frightened by it. The scary definitions are from Boost::Filesystem.

I used Boost to get the names of the files from current directory because it is much easier than dealing with native windows functions.

#!C++
    path pluginPath(argv[0]);
    pluginPath.remove_leaf();  
    directory_iterator end_itr;  
    for ( directory_iterator itr( pluginPath );itr != end_itr;++itr )  
    {  
       if (extension(itr->path().leaf()) == ".dll")  
       {  
          myKernel.loadPlugin(pluginPath.native_directory_string() + itr->filename());  
       }  
    }  

The function Kernel::loadPlugin takes a single string argument which is the name of the file we want to load.

#!C++
    Kernel::loadPlugin(std::string fileName);  

Loading the plug-ins is that easy. Now we get the classes from the loaded plug-ins:

#!C++
    vector<ShoeDriver*> shoeDrivers;  
    vector<HatDriver*> hatDrivers;  
    shoeServer->getAllDrivers(shoeDrivers);  
    hatServer->getAllDrivers(hatDrivers);

    vector<ShoeDriver*>::iterator sbegin = shoeDrivers.begin();
    vector<ShoeDriver*>::iterator send = shoeDrivers.end();
    vector<HatDriver*>::iterator hbegin = hatDrivers.begin();
    vector<HatDriver*>::iterator hend =hatDrivers.end();

    for_each(sbegin, send, useShoe);
    for_each(hbegin, hend, useHat);

We also have to create the functions useShoe() and useHat(). Note that you would need to use function protoypes to put these after main().

#!C++
    void useShoe(ShoeDriver* d)
    {
      Shoe* newShoe = driver->createShoe();  
      cout << newShoe->getName() << endl;  
      delete newShoe;  
    }

    void useHat(HatDriver* d)
    {
      Hat* newHat = driver->createHat();  
      cout << newHat->getName() << endl;  
      delete newHat;  
    }

The complete source code can be found in the distrobution, in the examples directory.
Happy pugging!


Related

Wiki: Animal Tutorial (old)