Skip to content

Final Notes

You’ve seen the basic usage of Pluma with a dummy example. This page give some extra notes and tips for the development of more interesting programs.

Built-in modules

On the introduction we said that our program was going to have a built-in warrior. It is really simple, we create the SimpleWarrior class exactly as we did the Eagle and Jaguar, but this one is included in the host application. On our main file we include it’s header and add a new SimpleWarriorProvider to our pluma object:

pluma::Pluma manager;
manager.acceptProviderType<WarriorProvider>();

manager.addProvider( new SimpleWarriorProvider() );

manager.load("plugins", "elite-warriors");

Code integration and organization tips

If you want to split an existing project into a core system plus pluggable modules that’s probably because you’re doing modular C++ code already. You are probably using interfaces (abstract classes) to not rely on specific implementations, you’re certainly using the advantages of polymorphism. If not then you really have to make some modifications before continuing.
So at this point I assume you have a couple of interfaces somewhere in your project that you want to turn into shared plug-in interfaces. As you previously seen it is made with two simple macros, but you don’t have to write them on every class (specially if for some reason you don’t even have access or permission to modify those files, for example if those classes are from some extern library). You can leave all interfaces untouched and just create a new header and source files where you include all interfaces that you want to share and generate the respective providers:

MyPublicInterface.hpp:

#ifndef MY_PUBLIC_INTERFACE
#define MY_PUBLIC_INTERFACE

#include "Interface1.hpp"
#include "Interface2.hpp"
#include "Interface3.hpp"
(...)

PLUMA_PROVIDER_HEADER(Interface1);
PLUMA_PROVIDER_HEADER(Interface2);
PLUMA_PROVIDER_HEADER(Interface3);
(...)
#endif

MyPublicInterface.cpp:

#include MyPublicInterface.hpp
PLUMA_PROVIDER_SOURCE(Interface1, 3, 1);
PLUMA_PROVIDER_SOURCE(Interface2, 4, 1);
PLUMA_PROVIDER_SOURCE(Interface3, 3, 2);
(...)

A similar process can be done for the plug-in side. You have a couple of classes that are going to be used as a plug-in. Those classes implements the interfaces that are already shared. Don’t touch those files, just create a header file where you put the macros. Create a cpp file for the connector function and include that header.

Simple, isn’t it? You perfectly separate the plug-in related code from your application code.

As for the host application, simple applications can directly use a vector of providers, but you are probably interested in something more sophisticated.  It’s good idea to create your own manager class. It has a Pluma object to load/unload libraries and some way of choosing which provider to use, based on application circumstances/needs. This way you encapsulate the libraries management and objects creation in one black box.

Custom Providers (advanced)

The default providers has a function “create” that calls the default constructor of your classes. If you don’t like this behavior you can define your own. Instead of the macro PLUMA_PROVIDER_HEADER, there’s two other macros for the same purpose, but that doesn’t define the create function:

PLUMA_PROVIDER_HEADER_BEGIN(TYPE)
    // your interface body here
PLUMA_PROVIDER_HEADER_END

Between those two macros you define the body of a normal class. Here’s an example:

// class WarriorProvider
PLUMA_PROVIDER_HEADER_BEGIN(Warrior)
    public:
        virtual Warrior* create(int energy) = 0;
        virtual void populate(std::vector<Warrior*>& army, int numSoldiers) = 0;
PLUMA_PROVIDER_HEADER_END

The cpp file containing a call to PLUMA_PROVIDER_SOURCE remains the same.

On the plug-in side (or built-in) you can’t use the PLUMA_INHERIT_PROVIDER macro because of the new behavior. That’s not a problem, just inherit the provider manually, as on this example:

class EagleProvider: public WarriorProvider{
public:
    Warrior* create(int energy){
        return new Eagle(energy);
    }

    void populate(std::vector<Warrior*>& army, int numSoldiers){
        (...)
    }

};

On the host side you can now use those functions:

pluma::Pluma pluma;
pluma.acceptProviderType<WarriorProvider>();
pluma.load("plugins", "elite-warriors");
std::vector<WarriorProvider*> providers;
pluma.getProviders(providers);
(...)

providers[0].populate( myArmy, 15 );

Conclusion

After learning the basic usage of the Pluma framework you got an idea of Pluma for real programs.
Built-in modules can be used for example as a default provider when plug-ins are inexistent, so you don’t have to code differently for when you’re using plug-ins and for when not.
The code related to the plug-in architecture can be isolated, your application code doesn’t have to rely on plug-in specific restrictions.

The tutorial ends here, I hope you found it useful ;)


Index || Host Side << Final Notes