Elm  2
ELM is a library providing generic data structures, OS-independent interface, plugins and XML.
Plugins

Classes

class  Plugger
 
class  Plugin
 

Detailed Description

Using a Plugin

ELM provides classes to implement plug-ins in an OS-independent way. To work-around some shortcomings found on some OS, ELM provides also an independent way to optionally represent dependencies between plug-ins based on textual file with ".ini" syntax.

A plug-in is a piece of code that provides some service based on an interface implemented by the plug-in and used by a third-party program. The retrieval of the plug-in, isolated in its own binary file, is based on its name and a list of directories or from an absolute path. The object is in charge of managing the plug-in is called a plugger and allows to plug many different plug-ins to implement the same service.

In ELM, the plugger is declared with a hook - an identifier allowing an application to support different kinds of services and matching plug-ins - and a version - for the current implemented plug-in interface. In the example below, the plugger provides a service named "net_plugin" with an interface version 1.0.0 (see elm::Version for more details).

#define NET_HOOK net_plugin
elm::system::Plugger net_plugger(ELM_STRING(NET_HOOK), Version(1, 0, 0), ".:/usr/local/net_plugins");

To open a specific plugin named, for example, "my_net_plugin", one has only to call the method Plugger::plug().

elm::system::Plugin *plugin = net_plugger.plug("my_net_plugin");

If the plugin is found, is loadable and has a compatible version, it is loaded, initialized and its instance is returned. If there is an error, a null pointer is returned and the method Plugger::lastErrorMessage() may give some information about the error.

if(plugin == NULL)
cout << "ERROR: " << plugger.lastErrorMessage() << io::endl;

Having just a plugin pointer does not provide any service. To do this, we have usually to define an interface that is implemented by the plug-in instance. Let's call it "NetPlugin": it must be derived from the elm::system::Plugin class.

class NetPlugin: public Plugin {
public:
NetPlugin(const make& maker): Plugin(make(name, version))
{ }
virtual void performService(void) = 0;
};

Then, we can cast the plug-in to its required interface and we can call the service:

NetPlugin *net_plugin = static_cast<NetPlugin *>(plugin);
net_plugin->performService();

When the plugin is no more needed, we can simply unplug it:

net_plugin->unplug();

Creating Plugins

To create a plug-in, one has to define a class implementing the service interface of the plugger to plug to. Let's continue the example started in the previous section. Notice that we must implement the pure virtual functions of the interface in order to be able to create an instance of the plugin class.

class MyNetPlugin: public NetPlugin {
public:
MyNetPlugin(void);
void performService(void) override;
};

Then, the constructor must pass to the elm::system::Plugin class the name of the service to be hooked to the right plugger with the service version supported by the plugin.

MyNetPlugin::MyNetPlugin(void): NetPlugin(make("my_net_plugin", Version(1, 0, 0))) {
}

Notice how the version is encoded in the plug-in code. If this plug-in is then used with an incompatible version of the plugger, say 2.1.0. The compatibility comparison will compare the version 1.0.0 (from the plug-in) with the version 1.2.0 (from the plugger) and will detect possible binary incompatibility and prevent the invalid linkage.

Then we need to provide a hook to let the plugger found the plugin object.

MyNetPlugin my_net_plugin;
ELM_PLUGIN(my_net_plugin, NET_HOOK);

The result source must then be compiled to produce a shared code with your preferred compiler For example, with GCC on a Unix OS.

gcc -shared -o my_net_plugin.so my_net_plugin.cpp

It is a good practice, for the plug-in interface provide, to provide a macro in the header file to provide the current version of the interface:

#define NET_VERS Version(1, 0, 0)

If this macro is used in a plug-in, this will ensure without any additional work that a plug-in is compatible with the compiled version of the plugger. If there is some evolution in the interface that is incompatible with the compiled plug-in, it will be detected and reported by the compiler:

MyNetPlugin::MyNetPlugin(void): NetPlugin(make("my_net_plugin", NET_VERS)) {
}

ELD Files

ELM Linkage Description Files (ELD) allows to circumvent limitations found on some OSes for the management of dynamic libraries. Basically, ELD files are textual files with ".eld" extension implementing the ".ini" file syntax. They must placed in the same directory as the plug-in binary they apply to and get the same name as the plug-in with the dynamic library extension replaced by ".eld".

As usual ".ini" files, they can contain as many sections as possible but ELM is only interested by the "elm-plugin" section that may contain the following definitions:

"path" definition allows to cope with the lack of support for symbolic links on some OSes.

"rpaths" and "libs" are used to handle easily dependencies between plug-ins and libraries when the OSes does not support linkage lookup path encoded in executable.

Paths found in "path", "libs" and "rpaths" can be prefixed by "$ORIGIN" that is replaced, at run time, by the path of the directory containing the considered plug-in binary.

Below is an example of ELD files for our example requiring two other libraries that should be found in the directory containing the plug-in or in directory "/usr/lib". It is named "net_plugin.eld" and put in the same directory as "net_plugin.so".

[elm-plugin]
libs=libxml2;libxslt
rpath=$ORIGIN;/usr/lib

Below another example implements an alias, "net_plugin_v2" to "net_plugin". The file is named "net_plugin_v2.eld":

[elm-plugin]
path=$ORIGIN/net_plugin

This last example links with library libxml2 from the OS path:

[elm-plugin]
libs=libxml2
elm::cout
io::Output cout
elm::imm::make
list< T > make(T a[], int n)
Definition: list.h:106
elm
Definition: adapter.h:26
elm::io::endl
const EOL endl
Definition: io_Output.cpp:880