Plugin management
Developing, loading and using plugins.
The PluginManager::
- Plugin manager version and plugin interface version checks to avoid unexpected behavior
- Both static and dynamic plugin support
- Plugin dependecies and aliases
- Usage checks, the manager doesn't allow plugin unload if there are any active plugin instances
This tutorial will give you a brief introduction into how plugins are defined, compiled and managed.
Plugin interface
Plugin interface is a class with virtual methods, defining the way how to work with a particular plugin.
Every plugin interface has to be derived from PluginManager::
Additionally, an interface string should be provided by overriding the PluginManager::""
and effectively makes the interface compatibility check a no-op.
To make lives of the users easier, we can define a list of paths where the plugins will be searched for using pluginSearchPaths(). The paths can be either absolute (for example hardcoding a system-wide installation path) or relative (relative to the executable file). In this case the plugins will be right next to the executable, so just a single entry with ""
will do. If we wouldn't specify the search paths, the user would need to pass a plugin search path to the plugin manager constructor.
class AbstractAnimal: public PluginManager::AbstractPlugin { public: static Containers::StringView pluginInterface() { using namespace Containers::Literals; return "cz.mosra.corrade.Examples.AbstractAnimal/1.0"_s; } static Containers::Array<Containers::String> pluginSearchPaths() { return {InPlaceInit, {""}}; } explicit AbstractAnimal(PluginManager::AbstractManager& manager, Containers::StringView plugin): AbstractPlugin{manager, plugin} {} virtual Containers::String name() const = 0; virtual int legCount() const = 0; virtual bool hasTail() const = 0; };
Plugin definition
Every plugin is represented by a class derived from a particular plugin interface. The plugin class is then registered as a static or a dynamic plugin. Every plugin also needs to have an associated metadata file, which contains information about plugin dependencies and optionally also plugin-specific data. Full specification of plugin metadata file syntax can be found in the PluginManager::
First we define one static plugin, which will be included in the application out-of-the-box:
class Canary: public AbstractAnimal { public: explicit Canary(PluginManager::AbstractManager& manager, Containers::StringView plugin): AbstractAnimal{manager, plugin} {} Containers::String name() const override { return "Achoo"; } int legCount() const override { return 2; } bool hasTail() const override { return true; } };
After defining the plugin we have to register it with the CORRADE_
CORRADE_PLUGIN_REGISTER(Canary, Canary, "cz.mosra.corrade.Examples.AbstractAnimal/1.0")
And a corresponding configuration file, Canary.conf
:
[data] name=I'm allergic to canaries!
Then we define one dynamic plugin. Note that the macro for registering dynamic plugin is the same, the only difference will be in CMakeLists.txt
, as you will see below. This way you can decide at compile time which plugins will be dynamic, which will be static, or, for example, which will be compiled directly into the library/application, so they can be used directly without the plugin manager.
class Dog: public AbstractAnimal { public: explicit Dog(PluginManager::AbstractManager& manager, Containers::StringView plugin): AbstractAnimal{manager, plugin} {} Containers::String name() const override { return "Doug"; } int legCount() const override { return 4; } bool hasTail() const override { return true; } };
CORRADE_PLUGIN_REGISTER(Dog, Dog, "cz.mosra.corrade.Examples.AbstractAnimal/1.0")
And a corresponding configuration file, Dog.conf
:
[data] name=A simple dog plugin
Plugin compilation
Requiring the Corrade package using find_package()
will define two useful macros for plugin compilation:
find_package(Corrade REQUIRED PluginManager) set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) corrade_add_plugin(Dog ${CMAKE_CURRENT_BINARY_DIR} "" Dog.conf Dog.cpp) if(CORRADE_TARGET_WINDOWS) target_link_libraries(Dog PRIVATE Corrade::PluginManager) endif() corrade_add_static_plugin(Canary ${CMAKE_CURRENT_BINARY_DIR} Canary.conf Canary.cpp)
The corrade_
The corrade_
Plugin management
Now it's time to initialize PluginManager::
In order to make the plugin manager find the static plugins, we have to import them with the CORRADE_main()
function). It takes a plugin name as an argument.
This example application will load plugin specified as command-line argument and then displays brief info about a given animal. For convenient argument parsing and usage documentation we used Utility::
int main(int argc, char** argv) { /* Import static plugin using the same name as in Canary.cpp */ CORRADE_PLUGIN_IMPORT(Canary) Utility::Arguments args; args.addArgument("plugin").setHelp("plugin", "animal plugin name") .setGlobalHelp("Displays info about given animal.") .parse(argc, argv); /* Initialize plugin manager with given directory */ PluginManager::Manager<Examples::AbstractAnimal> manager; /* Try to load a plugin */ if(!(manager.load(args.value("plugin")) & PluginManager::LoadState::Loaded)) { Utility::Error{} << "The requested plugin" << args.value("plugin") << "cannot be loaded."; return 2; } /* Instance of an animal */ Containers::Pointer<Examples::AbstractAnimal> animal = manager.instantiate(args.value("plugin")); Utility::Debug{} << "Using plugin" << '\'' + animal->metadata()->data().value("name") + '\'' << "...\n"; Utility::Debug{} << "Name: " << animal->name(); Utility::Debug{} << "Leg count:" << animal->legCount(); Utility::Debug{} << "Has tail: " << (animal->hasTail() ? "yes" : "no"); return 0; }
Note that here we're including Corrade/*.h
file as we implemented our own plugin interface and thus need the full template definitons. See Custom plugin interfaces and template definitions for more information.
Compile the application with a simple CMake add_executable()
command and don't forget to link in all the static plugins compiled above:
add_executable(PluginTest main.cpp) target_link_libraries(PluginTest PRIVATE Canary Corrade::PluginManager)
After a successful compilation we can run the application with plugin name as an argument:
$ ./PluginTest --help Usage: ./PluginTest [-h|--help] [--] plugin Displays info about given animal. Arguments: plugin animal plugin name -h, --help display this help message and exit $ ./PluginTest Canary Using plugin 'I'm allergic to canaries!' Name: Achoo Leg count: 2 Has tail: yes $ ./PluginTest Dog Using plugin 'A simple dog plugin' Name: Doug Leg count: 4 Has tail: yes
The full file content is linked below. Full source code is also available in the GitHub repository.