[Documentation] [TitleIndex] [WordIndex

Design and Implementation

Though the class_loader package is quite simple in its implementation, the code may be a bit tricky to understand internally. Hence some notes are given here about the design so as to give guidance to future maintainers.

Motivation

ROS has implemented the concept of plugins from early in its inception via the pluginlib package. Though pluginlib is adequate for loading plugins, it was decided to refactor the code to address some shortcomings as well as make it more maintainable. Here were some of the shortcomings of pluginlib:

The resulting solution was breaking pluginlib into two packages: a class loader to implement the loading/unloading of classes (class_loader) and the existing pluginlib which sits on top of it. pluginlib itself does not change API-wise so as not to break packages depending on it.

Dependency on Poco

class_loader depends on the open source Poco library as pluginlib for providing the underlying ability to load and unload classes from runtime libraries. Unlike the legacy pluginlib which utilized a modded version of Poco, class_loader uses the stock version (libpoco-dev). Also, the class_loader does not use Poco's ClassLoader class, but rather just the lower-level Poco::SharedLibrary class so as to provide a more convenient class registration technique than the one provided by Poco::ClassLoader.

Class Diagram

Below is a class diagram for class_loader. Note that not all classes are shown, only public interfaces are exposed, and some of the method parameters that are really passed by const & appear to be passed by value for sake of compactness.

alt text

Understanding Class Registration

The macro CLASS_LOADER_REGISTER_CLASS is used to register classes to be available to a ClassLoader when a library is loaded into memory. This mechanism is how the ClassLoader is able to perform introspection of the library. The underlying library that is being used to open libraries, Poco, has it's own class loader called Poco::ClassLoader which performs almost the same functionality as class_loader::ClassLoader. The difference is the way registration works.

What Makes a Plugin System Hard to Design?

It is helpful to understand why implementing a plugin system is hard. Here are some reasons:

Automated Registration Through C++ Static Variable Instantiation

In Poco::ClassLoader, the user is forced to register all of their classes within a single source file in one place. This can be a bit inconvenient, so class_loader improves upon this. Instead the user can register with each class and then mix and match classes across various libraries with no worry. The way CLASS_LOADER_REGISTER_MACRO works is that it expands out to a new struct type and a corresponding static global variable of the same type. The constructor of this class invokes:

which creates a new factory of type class_loader::class_loader_private::MetaObject<Base,Derived>. These factories are stored by the plugin system and used to create plugins via their create() method.

What this trick does is that when the library is loaded, the C++ standard dictates that global variables are instantiated first. This invokes the constructor of the static variable and in turn the registration function above. That means each registered class is automatically added to a globally available list.

What's Up With the Global Functions?

The core of class_loader is implemented within a set of global functions in the namespace plugins::plugins_private. Originally the idea was to make things in a class, but because of implementation issues, reverted having to make all the data structures globally available and having a class did not make sense. The cleaner result was just a set of global functions and writing the exposed interface in the class_loader::ClassLoader and class_loader::MultiLibraryClassLoader classes.


2024-09-28 14:05