Menu

Animal Tutorial

tutorial (2)
Alex

Tutorial

Plugin Model

There are several parts to the apugg plugin model:

  • Plugin Interface
  • Plugin Class
  • Driver
  • Server
  • Kernel

Plugin Interface

The 'Plugin Interface' is a class (usually pure-virtual) that the Plugin Class
in the plugin implements (through inheritance).

Plugin Class

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.

Driver

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.

Server

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.

Kernel

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.

Other Parts

There are two other parts that need to be created to use the plugin model:

  • Registration Method
  • Plugin Manager

Registration Method

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.

Plugin Manager

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'.

Writing a host

Plugin Interface and Driver

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.

Host Program (main)

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.

Writing a Plugin

Plugin Class and Driver

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.

Registration Method

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".

Compiling and Running

Compiling the Example

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.

Running the Example

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.

[Animal Tutorial (Part 2)]


Related

Wiki: Animal Tutorial (Part 2)
Wiki: Home

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.