First, you need to grab a copy of the apugg source code. You need to use at least version 0.8 or later.
| Release media | URL |
|---|---|
| GIT repo | git://git.code.sf.net/p/apugg/code |
| GIT repo online | http://sourceforge.net/p/apugg/code/ |
| Releases | http://sourceforge.net/projects/apugg/files/ |
The top-level directory contains some README files and a copy of this tutorial in Markdown markup. The examples are all in the example/ directory and all the actual PUGG library files are in the include/ directory.
PUGG is a header-only library, so all you need to do is include the .h files in your project somewhere. No installation required, and no extra DLLs to distribute!
All the PUGG classes are in the pugg namespace, so it doesn't even clutter up your application!
To use apugg, you write base classes in your application and then load sub-classes (plugins) from other libraries. You can then run methods provided through the common interface (the base class).
Note: all the source code is in the example/animals directory.
First, we need to write the base classes. Here is the source code for animal.h:
#include <pugg/Driver.h>
class Animal
{
public:
virtual void speak() = 0;
virtual bool canFly() = 0;
static const int ifaceVersion;
static const std::string serverName() { return "AnimalServer"; }
};
const int Animal::ifaceVersion = 4;
class AnimalDriver : public pugg::Driver
{
public:
virtual Animal* createAnimal() = 0;
};
What we see here is the definition of two classes - Animal and AnimalDriver.
The Animal class is the base class that all the plugins must derive from. It provides two pure virtual methods - speak() and canFly(). These methods are the 'interface' that the host uses to interact with the plugin.
The AnimalDriver class is a factory class. The one method it defines is again pure virtual and is used by the host to create an instance of the plugin class. It derives from pugg::Driver which defines another pure virtual function which will be discussed later.
The interface version (ifaceVersion) is used for plugin versioning - if the interface of the Animal class changes, we can change this number and only compatible plugins will be loaded.
The server name is the name of the interface (AKA server). This is used to make sure that only one type of plugin is registered with each interface.
plugin.cpp:
#include <pugg/Kernel.h>
#include <pugg/Server.h>
#include <pugg/Platform.h>
#include <iostream>
#include "plugin.h"
using namespace pugg;
PUGG_RegFnDefault
{
kernel.addDriver<AnimalDriver>(new CowDriver(), Animal::serverName(), Animal::ifaceVersion);
kernel.addDriver<AnimalDriver>(new PigDriver(), Animal::serverName(), Animal::ifaceVersion);
kernel.addDriver<AnimalDriver>(new EagleDriver(), Animal::serverName(), Animal::ifaceVersion);
}
First, we have includes for the PUGG classes Kernel and Server, as well as Platform.h.
We also include plugin.h for out base class definition.
Platform.h provides a set of macros that can be used to define a 'registration method' for the plugin. This method is called when the plugin is loaded, and registers the driver classes with the host program, so they can be used.
The macro PUGG_RegFnDefault defines a registration method with the default name. The method takes a single parameter - kernel. This is a reference to the Kernel object that the host program provides, and is used to register plugins.
addDriver takes three parameters - a pointer to a Driver* subclass, the name of the plugin interface, and the version of the plugin interface.
Plugin.h holds the declarations for our Cow animal.
#include "animal.h"
#include <iostream>
class Cow : public Animal
{
public:
virtual void speak()
{
std::cout << "Moo!\n";
}
virtual bool canFly()
{
return false;
}
};
class CowDriver : public AnimalDriver
{
public:
virtual std::string getName()
{
return "CowAnimalPlugin";
}
virtual Cow* createAnimal()
{
return new Cow;
}
};
class Pig : public Animal
{
public:
virtual void speak()
{
std::cout << "Oink!\n";
}
virtual bool canFly()
{
return false;
}
};
class PigDriver : public AnimalDriver
{
public:
virtual std::string getName()
{
return "PigAnimalPlugin";
}
virtual Pig* createAnimal()
{
return new Pig;
}
};
class Eagle : public Animal
{
public:
virtual void speak()
{
std::cout << "Shriek!\n";
}
virtual bool canFly()
{
return true;
}
};
class EagleDriver : public AnimalDriver
{
public:
virtual std::string getName()
{
return "EagleAnimalPlugin";
}
virtual Eagle* createAnimal()
{
return new Eagle;
}
};
The three plugin classes (Eagle, Pig and Cow) implement the interface of the Animal object and are the classes that are used by the host.
The driver classes implement two methods - getName() and createAnimal(). getName returns a unique string to describe the plugin, and createAnimal returns an instance of the plugin.
The host application is completely implemented in Host.cpp:
#include <vector>
#include <algorithm>
#include <iostream>
#include <pugg/Kernel.h>
#include <pugg/Server.h>
#include <pugg/Platform.h>
#include <pugg/LocalPlugin.h>
#ifdef PUGG_PLATFORM_LINUX
#include <pugg/LinuxSOPlugin.h>
#elif defined(PUGG_PLATFORM_WIN32)
#include <pugg/WindowsDLLPlugin.h>
#endif
#include <pugg/exception/all.h>
#include "../plugin/animal.h"
#include "host.h"
using namespace std;
using namespace pugg;
void useAnimal(AnimalDriver* driver)
{
Animal* animal = driver->createAnimal();
cout << "Animal: " << driver->getName() << "\n";
animal->speak();
cout << "Can fly: ";
if(animal->canFly()) cout << "Yes\n\n";
else cout << "No\n\n";
delete animal;
}
int main()
{
try
{
Kernel kernel;
vector<AnimalDriver*> animalDrivers;
kernel.addServer<AnimalDriver>(Animal::serverName(), Animal::ifaceVersion);
Plugin* plugin = 0; // if platform is not supported, loadPlugin will throw on null ptr.
#ifdef PUGG_PLATFORM_LINUX
plugin = new LinuxSOPlugin("./libanimals.so");
#elif defined(PUGG_PLATFORM_WIN32)
plugin = new WindowsDLLPlugin("./animals.dll");
#endif
kernel.loadPlugin(plugin);
plugin = new LocalPlugin<AnimalDriver>(Animal::serverName(), Animal::ifaceVersion, new PenguinDriver());
kernel.loadPlugin(plugin);
animalDrivers = kernel.getAllDrivers<AnimalDriver>(Animal::serverName());
for_each(animalDrivers.begin(), animalDrivers.end(), useAnimal);
return 0;
}
catch(pugg::PuggException* x)
{
cerr << x->what() << endl;
terminate();
}
}
OK. This is a bit of a handful. First, we start by including some standard headers, including two called 'LinuxSOPlugin.h' and 'WindowsDLLPlugin.h' depending on the current platform.
We then define a useAnimal() method to call the methods in the animal plugins.
The main() function declares a Kernel with version management info. We then declare a vector to hold all the Animals.
We call addServer() to add the server to the kernel, and then load the plugin using loadPlugin() (it has a very imaginative name). We then iterate through all the plugins and call useAnimal for all of them.
Now, how do we compile?
cd example/animals
mkdir build
cd build
cmake ..
make
And it should output...
Animal: CowAnimalPlugin
Moo!
Can fly: No
Animal: EagleAnimalPlugin
Shriek!
Can fly: Yes
Animal: PenguinAnimalPlugin
Honk!
Can fly: No
Animal: PigAnimalPlugin
Oink!
Can fly: No
Now that you have finished the tutorial, look through the doxygen docs that can be generated from the source. And of course, look through the source code for many more things to learn.