ImFusion C++ SDK 4.4.0
Plugin Architecture

Infrastructure and functionality to dynamically extend application functionality via shared libraries. More...

Collaboration diagram for Plugin Architecture:

Detailed Description

Infrastructure and functionality to dynamically extend application functionality via shared libraries.

Plugins are a mechanism to dynamically extend an application with functionality without the application a-priori knowing all plugins that will be available at runtime. On the OS/filesystem level, they correspond to a dynamically linked shared library (on Windows: DLL).

Note
Plugins of the ImFusion SDK are occasionally also referred to as "modules", especially in non-technical contexts.

Anatomy of an ImFusion Plugin

In the context of the ImFusion SDK a library implementing an ImFusion plugin must meet a set of certain requirements to be considered a plugin:

  • Implement a plugin-specific specialization of the PluginBase interface. The class must have a static member function const char* id() that returns a unique identifier of the plugin. For consistency, we recommend to use the following scheme: <Namespace>.<PluginName>, where <Namespace> is a consistent identifier for all plugins from a single author/vendor.
  • Export a C function with the signature ImFusion::PluginBase* imfusionCreatePlugin() that is used by PluginManager to instantiate the PluginBase specialization.
  • Export a C function with the signature const char* imfusionPluginId() that returns the unique plugin ID.
  • Export a global C variable const char* imfusionPluginDescriptor starting with the value defined by the IMFUSION_PLUGIN_MAGIC_STRING macro followed by a set of key-value pairs encoding metadata such as expected host application, minimum/maximum supported version of the host application, optional build type information and optional plugin version.
  • Optionally, use the PluginAutoRegistrationGuard helper class to implement automatic plugin self-registration on library load of the OS.

For most use cases you should not implement the last 4 items manually as an SDK user. Instead you shall use the IMFUSION_REGISTER_PLUGIN() macro implementing them for you.

Note
There can be only one plugin per shared library.

Plugin Lifetime

During the execution of the host application each ImFusion SDK plugin goes through a series of states:

  • When the host application starts all potential plugins are initially in the Unknown state. The shared library of the plugin exists in the filesystem but is not yet loaded/registered by the host application and therefore unknown to it.
  • Different events can change the state to Registered: Explicit loading via PluginManager::registerPlugin(), scanning a plugin folder for candidate libraries via PluginManager::registerPlugins(), or the OS automatically loading a plugin library which the host application was linked against and that support auto-registration. All these events will analyze the shared library, check if it matches the anatomy of an ImFusion plugin and if it is compatible with the host application. If any of these checks fail the corresponding plugin will be in Incompatible state.
  • In order to actually make use of a plugin it must be Initialized. Plugin initialization will make it register it with the host application on a logical level, for instance by populating factories or other extension points. This has to be requested explicitly by calling either PluginManager::initPlugin() for individual plugin IDs or PluginManager::initAllRegisteredPlugins(). If plugin initialization fails for any reason or if any of a plugin's dependencies cannot be initialized it will be in Failed state.

Implementing a new Plugin

// Subclass PluginBase in order to create a new plugin.
class ExamplePlugin : public PluginBase
{
public:
// Define this static member function that returns a unique identifier for your plugin.
static const char* id() { return "MyInstitutionName.ExamplePlugin"; }
// Name of the author of the plugin for informative purposes.
std::string author() const override { return "Institution Name"; }
// Description of the plugin purpose/functionality for informative purposes.
std::string description() const override { return "Examplary implementation of an ImFusion plugin"; }
// Optional list of plugin IDs on which this plugin depends.
// Initialization of this plugin will fail if any of the plugins in this list can not be loaded or initialized.
std::vector<std::string> dependencies() const override { return {}; }
protected:
// Perform plugin initialization, for instance by populating factories and registering custom types with the host application.
// It is critical that all non-trivial initialization happens in this function and not in the class constructor.
Status init() override
{
LOG_DEBUG("MyInstitutionName.ExamplePlugin", "Initializing plugin...");
SomeFactory::get().registerType(...);
SomeOtherFactory::get().register(...);
return Status::Success;
}
};
// Call the helper macro to define and export all necessary symbols, so that the host application can use the plugin.
// The second argument is the version number of the plugin for informative purposes (can be different of the version of the host application).
// The version is optional and can be omitted.
// This macro must be placed in a cpp file and *not* in a header file!
IMFUSION_REGISTER_PLUGIN(ExamplePlugin, "1.0");

Using Plugins in an Application

In order to use plugins in your application you must make sure to load and initialize them via the PluginManager. If the default behavior and plugin seach paths (cf. PluginManager::defaultPluginSearchDirectories()) are sufficient for your use case, you can do so with a single function call:

void applicationInit()
{
// Convenience function to register and initialize all plugins found in the default plugin search paths.
PluginManager::get().loadPlugins();
}

Alternatively, you can customize the behavior and split the registration of plugins and their initialization into separate steps. For instance, by scanning a directory for suitable plugin libraries and then initializing all that were found.

void applicationInit()
{
PluginManager::get().scanForPlugins({pathToPluginDirectory});
for (const auto& [pluginId, status] : PluginManager::get().initAllRegisteredPlugins())
{
LOG_ERROR("Could not initialized plugin " << pluginId);
}
}

Alternatively, if you can enumerate the list of plugins at build time, you can explicitly register and initialize each plugin individually.

If your application explicitly links against plugin libraries and those libraries use the default IMFUSION_REGISTER_PLUGIN() macro, then these plugins should automatically register themselves with PluginManager once loaded by the OS. In this case you can call PluginManager::initAllRegisteredPlugins() to initialize them without explicit enumeration.

See also

Classes

class  ImFusion::PluginBase
 Base interface to interact with a plugin that can be loaded into an application at runtime. More...
class  ImFusion::PluginManager
 Global singleton for loading ImFusion plugins from shared libraries and initializing them. More...
class  ImFusion::AlgorithmControllerFactory
 Interface for algorithm controller factories. More...
class  ImFusion::AlgorithmFactory
 Interface for algorithm factories. More...
class  ImFusion::CustomImage
 Base interface for custom Image types that can be converted from/to MemImages and are supported by SharedImage. More...
class  ImFusion::DataAnnotationFactory
 Interface for data annotation factories. More...
class  ImFusion::DataComponentFactory
 Factory singleton for creating DataComponents from string IDs. More...
class  ImFusion::ImFusionLibPlugin
 Specialized interface for a plugin for the ImFusionLib. More...
class  ImFusion::ImFusionPlugin
 Base class for plugins that can be loaded by the ImFusion SDK. More...
class  ImFusion::GlObjectFactory
 Factory singleton for creating GlObject from string IDs. More...
class  ImFusion::RendererFactory
 Static factory class to manage registered slice and volume renderers to be used in views. More...
class  ImFusion::DataDisplayHandlerFactory
 Factory class to maintain the list of registered DataDisplayHandlers ,. More...
class  ImFusion::Dicom::IOD_Registry
 A registry that assigns a IOD implementation to a SOP class UID. More...
class  ImFusion::SimilarityMeasureFactory
 The SimilarityMeasureFactory class provides a generic interface to any kind of SimilarityMeasure. More...

Macros

#define IMFUSION_REGISTER_PLUGIN_NO_AUTO(...)
 Helper macro to conveniently define and export the necessary functions and symbols for an ImFusion plugin but not implementing automatic self-registration of the plugin on OS library load.
#define IMFUSION_REGISTER_PLUGIN(...)
 Helper macro to conveniently define and export the necessary functions and symbols for an ImFusion plugin and also implement automatic self-registration of the plugin on OS library load.
#define IMFUSION_REGISTER_LEGACY_PLUGIN(PluginClassName, PluginID)
 Convenience macro to DLL-export a legacy ImFusionPlugin.

Macro Definition Documentation

◆ IMFUSION_REGISTER_PLUGIN_NO_AUTO

#define IMFUSION_REGISTER_PLUGIN_NO_AUTO ( ...)

#include <ImFusion/Core/Plugin.h>

Value:
IMFUSION_REGISTER_PLUGIN_EXPAND(IMFUSION_REGISTER_PLUGIN_NO_AUTO_IMPL(__VA_ARGS__, ""))

Helper macro to conveniently define and export the necessary functions and symbols for an ImFusion plugin but not implementing automatic self-registration of the plugin on OS library load.

Note
Make sure to place this macro in a cpp file and not in a header file.
See also
IMFUSION_REGISTER_PLUGIN()

◆ IMFUSION_REGISTER_PLUGIN

#define IMFUSION_REGISTER_PLUGIN ( ...)

#include <ImFusion/Core/Plugin.h>

Value:
namespace \
{ \
static ::ImFusion::PluginAutoRegistrationGuard g_imfusionPluginLoaderGuard = { \
&IMFUSION_REGISTER_PLUGIN_EXPAND(IMFUSION_REGISTER_PLUGIN_GET_CLASSNAME(__VA_ARGS__))::id}; \
}
#define IMFUSION_REGISTER_PLUGIN_NO_AUTO(...)
Helper macro to conveniently define and export the necessary functions and symbols for an ImFusion pl...
Definition Plugin.h:24

Helper macro to conveniently define and export the necessary functions and symbols for an ImFusion plugin and also implement automatic self-registration of the plugin on OS library load.

The PluginVersion argument is optional and can be omitted.

Note
Make sure to place this macro in a cpp file and not in a header file.
Warning
Because of the auto-registration feature it is critical that the plugin class constructor does not perform any non-trivial initialization. Otherwise, you might end up deadlocking the OS loader lock. Use IMFUSION_REGISTER_PLUGIN_NO_AUTO() to register an ImFusion plugin without auto-registration in case you cannot comply with this constraint.
Examples
ExamplePlugin.cpp.

◆ IMFUSION_REGISTER_LEGACY_PLUGIN

#define IMFUSION_REGISTER_LEGACY_PLUGIN ( PluginClassName,
PluginID )

#include <ImFusion/Base/ImFusionPlugin.h>

Value:
namespace \
{ \
class LegacyPluginWrapper : public PluginClassName \
{ \
public: \
static const char* id() { return PluginID; }; \
std::string author() const override { return "Unknown"; } \
std::string description() const override { return ""; } \
\
protected: \
Status init() override { return ImFusion::initLegacyImFusionPlugin(*this); } \
}; \
} \
IMFUSION_REGISTER_PLUGIN_NO_AUTO(LegacyPluginWrapper)
void init(Level globalMinimumLevel, Mode operationMode)
Initialize the logging framework.
Status
Status codes for CT operations.
Definition Utils.h:40
PluginBase::Status initLegacyImFusionPlugin(ImFusionPlugin &plugin)
Helper function to implement legacy plugin initialization previously done by Framework::registerPlugi...

Convenience macro to DLL-export a legacy ImFusionPlugin.

Legacy plugins will not self-register them on library load. They usually performed their initialization during class construction and therefore would be prone to deadlock otherwise.

Deprecated
Search Tab / S to search, Esc to close