Skip to content
bidachon edited this page Jul 30, 2015 · 26 revisions

Welcome to the PluginManager wiki!

Introduction

The Plug-in architecture is composed of a set of Core APIs and many loadable modules providing additional features or services to an application, referred to as plug-ins.

This level of modularity has a number of benefits:

  • Easier parallel feature development
  • The ability to mix and match features
  • Enable unit testing at module level
  • Speed-up prototyping and foster code re-use

Concepts

A plug-in is simply a code library that...

is linked/loaded at run-time.

  • exposes one or more C++ interfaces. We call them Extensions
  • requires zero or more C++ interfaces from other plug-ins. we call them Dependencies

diagram

Plug-ins generally provide some kind of service, feature or function. They often depend on other interfaces. Implementation of those interfaces are provided by other plug-ins.

Architecture

The Plugin Manager loads all plugins (shared libraries) and connect the Extensions with the Dependencies. The list of plugin to be loaded is provided by a configuration file (.json file)

Json example: { "configuration": { "description": "Demo application", "author":"Nicolas Robert" }, "plugins":["../../plugins/libUniverseAnswerGeneratorPlugin", "../../plugins/libGeneratorManagerPlugin", "../../plugins/libDummyGeneratorPlugin"] }

Plugin Manager class diagram

How To

Define your Interface

Your interface must inherit IPluginInterface and provide an implementation for the id() and rttiMatch() method. note: make sure than you interface id is unique across all interfaces

class IGeneratorManager: public IPluginInterface
{
public:
    virtual ~IGeneratorManager() {}
    virtual QWidget* widget() = 0;
    virtual InterfaceId id() const
    {
        return InterfaceId("IGeneratorManager");
    }
    virtual bool rttiMatch(const char *str) const
    {
      return (::strcmp(id().name().c_str(), str) == 0);
    }
};

Define your implementation

Must inherit QObject along with your Interface. Do not forget the Q_OBJECT macro. The method addNumberGenerator() below will be called by the plugin to transmit the IPluginInterface given by the Plugin Manager.

class GeneratorManagerImpl: public QObject, public plugin::interfaces::IGeneratorManager
{
    Q_OBJECT
public:
    GeneratorManagerImpl();
    ~GeneratorManagerImpl();
    QWidget* widget();

    void addNumberGenerator(QSharedPointer<plugin::interfaces::INumberGenerator> gen)
    {
        m_gens.append(gen);
    }

public slots:
    void generateNumbers();

private:
    QWidget *m_root;
    QTextEdit *m_textEdit;
    QList< QSharedPointer< plugin::interfaces::INumberGenerator> > m_gens;
};

note: this Class inherits QObject because it has slots. It is not mandatory. See UniverseAnswerGeneratorPlugin as an example.

Define your Plugin

The Plugin itself must inherit IPluginLib.

First, set your interface as a singleton:

static QSharedPointer<GeneratorManagerImpl> generatorManager()
{
  static QSharedPointer<GeneratorManagerImpl> theGeneratorManager;
    if ( !theGeneratorManager ) {
      theGeneratorManager.reset( new GeneratorManagerImpl() );
    }
  return theGeneratorManager;
}

You must also declare:

  • your extensions (The Interfaces for which your plugin will provide an implementation
  • your dependencies (The interfaces that you plugin requires)
GeneratorManagerPlugin::GeneratorManagerPlugin()
{
    m_extensions.push_back(new plugin::Extension(generatorManager()));
    m_dependencies.push_back(new plugin::Dependency(0,plugin::DEPENDENCY_UNLIMITED,plugin::InterfaceId("INumberGenerator")));
}

Finally, you should handle the dependencies that will be given to your plugin by the Plugin Manager:

void GeneratorManagerPlugin::connectExtension(QSharedPointer<plugin::interfaces::IPluginInterface> iface)
{
    //cast away Tom!
    QSharedPointer<plugin::interfaces::INumberGenerator> nbGen = qSharedPointerCast<plugin::interfaces::INumberGenerator>(iface);
    generatorManager()->addNumberGenerator(nbGen);
}

If you have more than one dependency, you should try to cast to all the required interfaces until you find the right one.

There is a cardinality notion when it comes to defining dependencies. You can have a optional dependency (0..n), or a hard one (1..1), (2..3) etc. IT can also be unlimited, and in this case, the Plugin Manager will provide all the implementation of a given interface.

Note: It must also inherit QObject. I will try to move this dependency up the tree in order to simplify the plugin creation.