SEAL modules are shared libraries with a special entry point through which the module properties are advertised. They are registered to a location known to the plug-in manager to make them available at run-time. Follow these steps to create a module:
Let's assume you wish to extend SampleFactory, a plug-in factory creating objects of type SampleObject. You add a new plug-in MyObject that inherits SampleObject. SampleFactory::create() specifies the arguments passed to MyObject constructor; see the factory documentation for details. This example follows the SEAL coding conventions, please refer to your own project guidelines for more details.
Once you have code for MyObject, you need to define the plug-in manager entry point and to register your MyObject to SampleFactory. Do so by creating a src/module.cpp with the following contents:
//<<<<<< INCLUDES >>>>>> #include "YourPackage/MyObject.h" #include "SomePackage/SampleFactory.h" #include "PluginManager/ModuleDef.h" //<<<<<< PRIVATE DEFINES >>>>>> //<<<<<< PRIVATE CONSTANTS >>>>>> //<<<<<< PRIVATE TYPES >>>>>> //<<<<<< PRIVATE VARIABLE DEFINITIONS >>>>>> //<<<<<< PUBLIC VARIABLE DEFINITIONS >>>>>> //<<<<<< CLASS STRUCTURE INITIALIZATION >>>>>> //<<<<<< PRIVATE FUNCTION DEFINITIONS >>>>>> //<<<<<< PUBLIC FUNCTION DEFINITIONS >>>>>> //<<<<<< MEMBER FUNCTION DEFINITIONS >>>>>> DEFINE_SEAL_MODULE (); DEFINE_SEAL_PLUGIN (SomeFactory, MyObject, "MyObject Registration Name");
Include the headers for all types involved: all the plug-in types you want to register, all the factories you are registering to, and finally PluginManager/ModuleDef.h for the macro definitions.
Module and plug-in registration is handled by macros. Define the module entry point with DEFINE_SEAL_MODULE() macro. Define new plug-in types with DEFINE_SEAL_PLUGIN(category, type, name), one invocation for each plug-in type, each on a source line of its own. The name argument is the name by which MyObject should appear in the plug-in catalog; normally some human-readable name as it gets used in configurations. Avoid non-trivial global constructors in your package. The application is often incompletely initialised at the time the plug-in is loaded. In particular, do not assume any framework has had a chance to initialise yet. Normally the world looks sane by the time plug-in objects are instantiated, but it is up the clients of the factories to ensure that. See the factory documentation on what you can expect.
The plug-in manager defines three module events: query, attach and detach. Query event occurs when the plug-in manager detects a new module and queries the module for its capabilities and registers the results into the cache. Normally there is no reason to add actions to this category. Attach occurs when the module is loaded and the plug-in manager wants to instantiate factories for the objects in the module. Use an attach action to cause side-effects, such as register objects to factories outside the plug-in manager architecture. Detach occurs when the plug-in manager is about to unload the module. If you did something in attach, undo it in detach.
Use SEAL_MODULE_X_ACTION() to register custom actions to execute at the module events; X is either QUERY, ATTACH or DETACH. The macros take a single argument, a parenthesised argument list to CreateCallback. For example, to invoke function foo at attach time, use:
SEAL_MODULE_ATTACH_ACTION ((&foo));
The plug-in manager also supports a special kind of non-object plug-in: capabilities (see PluginCapabilities). They essentially designate libraries that ought to be loaded for their side-effects. Usually the global constructors of the library do something to get the library registered to some other service, no factories need to be registered with the plug-in manager.
The SEAL plug-in manager allows a library to define two entry points, one for normal plug-ins as explained above, and another for the capabilities. Any one library can have either or both entry points, and they will be used as necessary for registration. If the library defines only capabilities, it doesn't need to link against the plug-in manager at all.
To define a capabilities entry point, add code like this to your module.cpp:
extern "C" void SEAL_CAPABILITIES (const char **&names, int & /* count */) { static const char *list [] = { "Capability 1", "Capability 2", 0 }; names = list; // optional: count = 2; }
The function gets two arguments it should set. names should be set to the array of capabilities the library exports. count should be set to the number of elements in names. However, if names is set to an array that is terminated by a null pointer as in the example above, then count doesn't have to be set at all. If count is set, that many values in names must be valid. It is ok to set names to an empty array, i.e. just point it to a null pointer.
To register your module as a plug-in, add src/BuildFile with just the following contents:
include $(LOCALTOP)/config/module.mk
This will cause the module to be registered into the project's module database (usually arch/lib/modules). The project must already have config/module.mk. To add it, please refer to the project module guide.
Modify the BuildFile in your package (not the one you just added to src) to use SEAL plug-in manager and any other libraries your code needs. In a LCG project it would look like this:
<External ref=SEAL use=Foundation/PluginManager> <Use name=SomeSystem/SomePackage> <Include_Path path=.> <Export> <Lib name=MyPackageName> </Export>
The module must be linked with all its dependencies and must not have any unresolved symbols, or it will fail to load. Note that your project must be configured to link shared libraries against all their dependent shared libraries, or at least to do so for plug-in modules (config/compilers.mk in SEAL does so for whole of SEAL).
It is perfectly valid to link modules against each other; a SEAL module is just a normal dynamically loadable shared library. If you wish to allow others to link against your module, <export> the library created by the package.
Build the module with "scram build". You should see your library linked against other libraries and the creation of a simple text file in arch/lib/modules. Note that in SEAL you might have to introduce your package into the top level build order to get it to build at appropriate time; for details please see the new package how-to.
Once your package has been built, verify that the module loads, its properties are discovered and the plug-in objects can be instantiated. Your project should provide a program that can load modules and list all registered plug-ins. SealPluginDump (and SealPluginRefresh) is conveniently available for just this purpose. Normally you would run something like this:
$ eval `scram runtime -sh` $ SealPluginDump
You should see a message "Note: loading libMyPackageName.so". If the module has a problem, you will see a warning and as much error trace history that is available, e.g. the name of a missing symbol. Note that if your module has a problem, it will be marked bad in the cache and the system will not try load it again until you update the library (and the registration file, as that is the only way the plug-in manager knows the library has changed). Common causes for errors include forgetting to link against some other library (missing use in your BuildFile), forgetting to run moc in Qt-based programs, and forgetting to define all the virtual functions.
Make sure to add a test to exercise your plug-in object. Please refer to the documentation of the plug-in factory to learn how to instantiate objects of the category. Sometimes just a simple test program like this will do:
//<<<<<< INCLUDES >>>>>> #include "YourPackage/MyObject.h" #include "SomePackage/SomeFactory.h" #include "PluginManager/PluginManager.h" #include "classlib/debug.h" //<<<<<< PRIVATE DEFINES >>>>>> //<<<<<< PRIVATE CONSTANTS >>>>>> //<<<<<< PRIVATE TYPES >>>>>> //<<<<<< PRIVATE VARIABLE DEFINITIONS >>>>>> //<<<<<< PUBLIC VARIABLE DEFINITIONS >>>>>> //<<<<<< CLASS STRUCTURE INITIALIZATION >>>>>> //<<<<<< PRIVATE FUNCTION DEFINITIONS >>>>>> //<<<<<< PUBLIC FUNCTION DEFINITIONS >>>>>> //<<<<<< MEMBER FUNCTION DEFINITIONS >>>>>> int main (int, char **argv) { Signal::handle_fatal (argv [0]); SampleObject *p; seal::PluginManager::get ()->initialise (); VERIFY (p = SomeFactory::get ()->create ("MyObject Registration Name")); delete p; return EXIT_SUCCESS; }