There are several parts to the apugg plugin model:
The 'Plugin Interface' is a class (usually pure-virtual) that the Plugin Class
in the plugin implements (through inheritance).
The 'Plugin Class' is the class in the plugin that implements the Plugin
Interface. This is the class that 'does the work' for the plugin.
The 'Driver' is a special class in the plugin. There is one Driver for each
Plugin Class. Each Driver inherits from a class declared by the host that defines
a pure-virtual method that returns a pointer to the Plugin Class. Each Driver
in the plugin is also called a 'factory class', because its job is to 'manufacture'
instances of the Plugin Class.
A 'Server' is a class that manages all the Drivers and Plugin Classes that
implement a Plugin Interface. This class is usually hidden, but it can be thought
of as an individual 'plugin subsystem' of the host program. The Server is also
in charge of versioning - making sure that only compatible plugins are added.
There is usually one 'Kernel' in the Host program. This is a special class that
manages all the Servers, and also is the main API class that is used to load
plugins.
There are two other parts that need to be created to use the plugin model:
To get around C++ name mangling, each plugin library needs to export a C method
called a 'Registration Method'. The job of this method is to 'register' Driver
classes to the Server that manages the Plugin Interface that the Plugin Classes
implement.
The 'Plugin Manager' is a class that manages an individual plugin 'unit' - this
is usually a shared library or DLL, but it could be a Plugin Class that is
defined in the host - a 'host-local plugin'.
Let's start with a Plugin Interface:
#include <string> // std::string 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;
This interface declares two methods that Plugin Classes should implement:
virtual void speak() // Output a sound that the Animal makes virtual bool canFly() // Can the Animal fly?
We need to write a Driver subclass for the interface:
#include <pugg/Driver.h> // Driver class AnimalDriver : public pugg::Driver { public: virtual Animal* createAnimal() = 0; };
This class defines a method (createAnimal) that the host can use to create
Plugin Class instances.
The program that actually uses Plugin Classes looks like this:
#include <vector> // std::vector #include <algorithm> // for_each #include <pugg/Kernel.h> // Kernel #include <pugg/Plugin.h> // Plugin Manager #include <pugg/Platform.h> // PUGG_PLATFORM_* #include "animal.h" // Animal and AnimalDriver using namespace pugg; // apugg namespace using namespace std; int main() { Kernel kernel; vector<AnimalDriver*> animalDrivers; kernel.addServer<AnimalDriver>(Animal::serverName(), Animal::ifaceVersion); Plugin* plugin = 0; // Pointer to a Plugin Manager #if defined(PUGG_PLATFORM_LINUX) plugin = new LinuxSOPlugin("./libanimals.so"); // Linux plugin class #elif defined(PUGG_PLATFORM_WIN32) plugin = new WindowsDLLPlugin("./libanimals.dll"); // Windows plugin class #endif kernel.loadPlugin(plugin); animalDrivers = kernel.getAllDrivers<AnimalDriver>(Animal::serverName()); for_each(animalDrivers.begin(), animalDrivers.end(), useAnimal); return 0; }
First, we need to create a Kernel object. This object manages the apugg library,
and is the class we use to add Servers and Plugin Managers.
Next, we declare a vector of AnimalDriver pointers. This will hold the list of
all the AnimalDrivers that are registered by plugins.
We then add a new Server to the kernel, with the interface name and version that
we defined in the Animal class.
The next few lines create a new Plugin Manager with the right class and filename
depending on the current platform.
Next, we load the plugin manager into the kernel and query it for a list
of Drivers that have been registered. These methods will be those from the
library we loaded with the Plugin Manager.
We can now iterate over the list of Drivers and call useAnimal() for each one.
#include <iostream> // std::cout #include "animal.h" // AnimalDriver, Animal using namespace std; void useAnimal(AnimalDriver* driver) { Animal* animal = driver->createAnimal(); // Get an instance of the Animal subclass cout << "Animal: " << driver->getName() << "\n"; // Output the name of the Animal animal->speak(); // Tell the Animal to speak cout << "Can fly: "; if(animal->canFly()) cout << "Yes\n\n"; else cout << "No\n\n"; delete animal; }
This method uses the Driver to create a new Animal subclass instance. This instance
was defined in the plugin, so all the pure-virtual methods are now defined.
The driver->getName() method is defined in the pugg::Driver class that AnimalDriver
inherited from, and is implemented by the plugin Driver class.
The Plugin Class is the class in the Plugin that inherits from Animal (it
implements the plugin interface).
#include <iostream> // std::cout #include "animal.h" // Animal class Cow : public Animal { public: virtual void speak() { std::cout << "Moo!\n"; } virtual bool canFly() { return false; } };
We also need an AnimalDriver subclass for the Cow class:
#include "animal.h" // AnimalDriver class CowDriver : public AnimalDriver { public: virtual std::string getName() { return "CowAnimalPlugin"; } virtual Cow* createAnimal() { return new Cow; } };
This class implements the createAnimal method from AnimalDriver to return an
instance of the Plugin Class (Cow).
It also implements pugg::Driver::getName that returns a unique name for the
plugin. It must be unique. Really.
We need to write a Registration Method - a C-linked method that registers all
our Drivers to the kernel.
#include <pugg/Kernel.h> // Kernel #include <pugg/Platform.h> // PUGG_RegFnDefault #include "plugin.h" PUGG_RegFnDefault { kernel.addDriver<AnimalDriver>(new CowDriver(), Animal::serverName(), Animal::ifaceVersion); }
PUGG_RegFnDefault is a preprocessor-define from pugg/Platform.h that defines a
function with a single parameter (Kernel& kernel). It turns into something like
this:
extern "C" void registerPlugin(Kernel& kernel)
On Windows, it has an extra __declspec(dllexport)
in there after extern "C"
.
In the examples/animals
directory in the apugg distribution, run cmake and
then build the project/run make/do whatever you usually do with your build
system.
This should create two files:
libanimals.X
where X is either so
or dll
depending on your platform.animal_host
with a .exe
on the end on Windows.To run the example, run animal_host
in a terminal.
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
It outputs a bit more than just the Cow messages because the animals example
contains a bit more than we showed above.