Menu

PluginsAndRegistration

Tomassino Ferrauto Stefano Nolfi
Prev: Plugins, components and resources Up: Plugins, components and resources Next: Configuring components

Table of contents

Creating a plugin and registering components

This page shows in detail how to create a FARSA plugin. The procedure described here is the current one (which is availabe from FARSA version 1.3 onward). The documentation for the old way of writing plugins can be found at the following pages (please note that they are no longer updated):

Introduction

FARSA has a modular architecture. One of the key aspects of the modular FARSA architecture is that it is possible to add functionalities by writing plugins. A plugin is basically a shared library that is loaded at runtime by the total99 executable and that provides additional components. The components in a plugin are registered so that they can be referenced from configuration files. A plugin can also make use of classes defined in other plugins: when loaded by total99 all dependencies are loaded as well. The following sections will show how to write both the source code of the plugin and the CMake script to compile it.

Source code

To be able to generate a plugin the source code must respect the following requirements:

  • all header files must have one of the following suffix: .h, .hpp, .hxx or .hh;
  • no source file should be called plugin_main.h or plugin_main.cpp;
  • macros should be added to the definition of classes (see below).

The first thing to do is to make sure that all headers in the plugin have a supported extension, namely one of .h, .hpp, .hxx or .hh. If they don't please change the extension to one of the supported ones, otherwise cmake scripts will not work properly.

The next step is to remove or rename files named plugin_main.h or plugin_main.cpp. This is a fundamental step, as the presence of these files indicates to the build process that the old way of writing plugins is being used. In the current procedure, those files will be automatically generated by the farsapluginhelper command when building the plugin.

The following code fragment shows a typical class defined in a FARSA plugin.

:::C++
#include "farsaplugin.h"

...

class FARSA_PLUGIN_API ExampleClass : public farsa::Component
{
    FARSA_REGISTER_CLASS(Component)

...
};

The FARSA_PLUGIN_API macro is needed to export symbols from the resulting library, as you would normally do with a dynamic library. If the class is a template class or only has inline functions, you should use FARSA_PLUGIN_TEMPLATE instead of FARSA_PLUGIN_API. If you don't want to export the class you can use FARSA_PLUGINS_INTERNAL. In this case it will not be possible to use the class in other plugins (this should be almost never needed). You should always use one of FARSA_PLUGIN_API, FARSA_PLUGIN_TEMPLATE or FARSA_PLUGINS_INTERNAL when declaring classes in a plugin. The other macro to use is FARSA_REGISTER_CLASS. This is needed to register the class so that it can be instantiated from a configuration file, so you only need to use this macro if the class actually has to be registered (i.e. if it is a component). The argument of the macro is the name of the parent class (multiple inheritance is not supported, use only the base class that is also a component, here). This macro should be put at the beginning of the class declaration, before any class member is declared, as in the example above. If a class uses FARSA_REGISTER_CLASS, it is also mandatory to use one of the FARSA_PLUGIN_API, FARSA_PLUGIN_TEMPLATE or FARSA_PLUGINS_INTERNAL macro. For all classes whose declaration respects the guidelines given above, the build process will automatically generate instructions to register components when the plugin is loaded. All files with classes declaration should also include the farsaplugin.h header (as in the example above) because macros are defined there.

CMake script

Each plugin should have its own CMakeLists.txt that provide the information required by CMAKE to generate the files required by the compiler. CMake is a powerful tool which allows for complex compilation procedures. In the majority of the cases you can duplicate and re-use any of the CMakeLists.txt files provided with the examples or the one included below. In this section however we provide a full description of the meaning of all instructions.

:::CMake
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )

PROJECT( MyPlugin )

FIND_PACKAGE( FARSA REQUIRED )
SET( FARSA_USE_EXPERIMENTS TRUE )
INCLUDE( ${FARSA_USE_FILE} )

IF( FARSA_USE_QT5 )
    CMAKE_MINIMUM_REQUIRED( VERSION 2.8.9 )
ENDIF( FARSA_USE_QT5 )

FILE( GLOB SW_SRCS ./src/*.cpp )
FILE( GLOB SW_HDRS ./include/*.h )

IF( NOT FARSA_USE_QT5 )
    QT4_WRAP_CPP( SW_SRCS ${SW_HDRS} OPTIONS "-nw" )
ENDIF( NOT FARSA_USE_QT5 )

INCLUDE_DIRECTORIES( ./include )

ADD_FARSAPLUGIN( ${PROJECT_NAME} ${SW_SRCS} ${SW_HDRS} )

FARSA_PLUGIN_DEPENDENCIES( ${PROJECT_NAME} depPlugin1 depPlugin2 )

FARSA_PLUGIN_INSTALL_CONF( ${PROJECT_NAME} confDir )

FARSA_PLUGIN_INSTALL_CMAKE_SCRIPT( ${PROJECT_NAME} scriptName )

We will now analize all the instructions in the script.

:::CMake
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )

The first line of the file tells which is the minimum allowed version of CMake. In this case any release after the 2.6 can be used for this script.

:::CMake
PROJECT( MyPlugin )

This line simply sets the name for the project. Of course you need to change it if you want to adapt this file to your own experiment. This name will also be used as the name of plugin that will be created (see below).

:::CMake
FIND_PACKAGE( FARSA REQUIRED )
SET( FARSA_USE_EXPERIMENTS TRUE )
INCLUDE( ${FARSA_USE_FILE} )

The three lines above tell CMake that this project uses the FARSA framework. FARSA comes with a set of CMake-related files that ease writing CMake scripts. The first line loads a file (FARSAConfig.cmake) which contains some configuration variables. The second line sets the FARSA_USE_EXPERIMENTS variable to TRUE. This means that this project is a FARSA experiment plugin and has to be linked against experiment library. The last line includes the rules in the file whose name is in the FARSA_USE_FILE variable. This file uses the value of some variables in FARSAConfig.cmake (like FARSA_USE_EXPERIMENTS) to set up all that’s needed to compile the project.

:::CMake
IF( FARSA_USE_QT5 )
    CMAKE_MINIMUM_REQUIRED( VERSION 2.8.9 )
ENDIF( FARSA_USE_QT5 )

These lines are only needed if you plan to compile FARSA and your plugin with QT version 5.0 or above. They simply increase the minimum required version of CMake.

:::CMake
FILE( GLOB SW_SRCS ./src/*.cpp )
FILE( GLOB SW_HDRS ./include/*.h )

These two lines create two variables (SW_SRCS and SW_HDRS) containing the source files and the header files for the project respectively. More in detail they look for all files matching a certain pattern. For example the first line looks for all files whose extension is .cpp in the src directory. If in your project you want to use a different directory structure, you need to change these two lines to match your directory structure.

:::CMake
IF( NOT FARSA_USE_QT5 )
    QT4_WRAP_CPP( SW_SRCS ${SW_HDRS} OPTIONS "-nw" )
ENDIF( NOT FARSA_USE_QT5 )

These lines are needed in case you plan to compile FARSA and your plugin with QT version 4. The QT4_WRAP_CPP instructs CMake to generate moc files from the headers in the variable SW_HDRS and to add the name of the generated files to the variable SW_SRCS. When using QT version 5 or above CMake takes care of automatically generate the moc for all headers (that's why we need to use the IF). Please refer to QT documentation for information on what are moc files and why they are needed.

:::CMake
INCLUDE_DIRECTORIES( ./include )

This line tells CMake that the directory include contains header files. This is needed so that headers inside that directory are correctly found when included in source files. If in your plugin headers are at a different path, you need to change this instruction.

:::CMake
ADD_FARSAPLUGIN( ${PROJECT_NAME} ${SW_SRCS} ${SW_HDRS} )

This is the most important line in the file and is the one that actually instructs CMake to create the plugin. The first parameter is the name of the plugin (here set equal to the name of the project, stored in the PROJECT_NAME variable), the following parameters are the source and header files needed to compile the plugin (stored respectively in the SW_SRCS and SW_HDRS variables that were set above).

:::CMake
FARSA_PLUGIN_DEPENDENCIES( ${PROJECT_NAME} depPlugin1 depPlugin2 )

If your plugin depends on another FARSA plugin, you also need to add the FARSA_PLUGIN_DEPENDENCIES command to explicitate the dependency. The first parameter is the name of the plugin (the same you used in the ADD_FARSAPLUGIN command), the following parameters are the plugins on which your plugin depends. Note that you have to make sure that the plugins you depend upon are installed (see below). The FARSA_PLUGIN_DEPENDENCIES macro must always follow the ADD_FARSAPLUGIN macro.

:::CMake
FARSA_PLUGIN_INSTALL_CONF( ${PROJECT_NAME} confDir )

This line of the script instructs CMake to install configuration files for the plugin. This step is optional. The first parameter is the name of the plugin (the same you used in the ADD_FARSAPLUGIN command), the following one is a directory containing the configuration files to install. If the directory path is relative, it is taken as relative to the plugin source directory. The content of confDir (including subdirectories) is copied into the FARSA system directory with plugin configurations, in a subdirectory with the same name of the plugin. The FARSA_PLUGIN_INSTALL_CONF macro must always follow the ADD_FARSAPLUGIN macro.

:::CMake
FARSA_PLUGIN_INSTALL_CMAKE_SCRIPT( ${PROJECT_NAME} scriptName )

The last line of the script instruct CMake to install the CMake script for the plugin. This step is optional (it is supported in svn revision 990, it will be in the release of FARSA after 1.3.0). This command is needed for plugins on which other plugins may depend so that, for example, external libraries can be added to compilation. Once installed, the script is loaded automatically by FARSA_PLUGIN_DEPENDENCIES and the name of the plugin that is being generated is passed in the FARSA_INCLUDING_PLUGIN_TARGET_NAME cmake variable. The script name is changed to be plugin_name.cmake. Only one script for each plugin is supported.

Compilation and installation

To compile the plugin you can follow the instruction in the Compiling an Experiment Plugin page. If your plugin is meant to be used by other plugins, you need to install it. How this is done depends on what you use to compile the plugin. For example if you compile the plugin using Makefiles, you can simply use make install, if you use Visual Studio there is an INSTALL target and so on.

Defining functions to be executed before or after the plugin

It is also possible to provide custom function to be executed before or after the classes of the plugin are registered (this is available starting from svn revision 989, it will be in the release of FARSA after 1.3.0). You should generally not need this, but, in case your code requires it, you can use the following macros in your header:

:::C++
#include "farsaplugin.h"

...

FARSA_PRE_REGISTRATION_FUNCTION(funcname1)
FARSA_POST_REGISTRATION_FUNCTION(funcname2)

The first macro (FARSA_PRE_REGISTRATION_FUNCTION) says that the function funcname1 will be executed before classes are registered, while the second macro (FARSA_POST_REGISTRATION_FUNCTION) specifies that the function funcname2 will be executed after classes are registered. You can use those macros more than once to specify all the functions that need to be executed (the actual execution order is unspecified). The following conditions must be respected:

  • the macros must be put in a header file (those not in a header will be ignored);
  • the functions must take no parameters;
  • the fully qualified name of functions must be used, no matter where the macro is (e.g. to register the function myns::f you must use myns::f even if the macro is called inside the namespace myns);
  • functions must be declared in the header where the macro is called or in a header that is included (directly or indirectly) from the one in which the macro is called.

Limitations

The main limitation is that template classes are not correctly registered when used the approach for plugins described here. The old approach does not have this limitation because registration was performed by hand. A possible workaround is to create a non-template subclass for every template instantiation that has to be registered (obvoiusly, only template instantiations can be registered). Then use the first non-template class in the hierarchy as the parent class passed to the FARSA_REGISTER_CLASS macro. You can take a look at the RobotIRVirtualSampling code for an example (look at the end of the robotvirtualirsamplingexperiment.h file). Another limitation is that classes to register must be in the global namespace (this limitation has been removed in svn revision 953, it will be in the release of FARSA after 1.3.0).


Related

Manual: ComponentsConfig
Manual: ComponentsPluginAndResources
Manual: CustomSensorMotor
Manual: Home
Manual: Plugin Development - subclassing FarsaPlugin
Manual: Plugin Development - the CMakeLists.txt

Discussion

Anonymous
Anonymous

Add attachments
Cancel





MongoDB Logo MongoDB