Channels ▼


Building Your Own Plugin Framework: Part 3

Platform Services

Platform services are services provided to the plugins by your system or application. I call them "platform services" because the generic plugin framework can service as a platform for plugin-based systems. The PF_PlatformServices struct contains the version, the registerObject function pointer and the invokeService function pointer; see Example 1.


typedef apr_int32_t (*PF_RegisterFunc)(const apr_byte_t * nodeType, const PF_RegisterParams * params);
typedef apr_int32_t (*PF_InvokeServiceFunc)(const apr_byte_t * serviceName, void * serviceParams);
typedef struct PF_PlatformServices
  PF_PluginAPI_Version version;
  PF_RegisterFunc registerObject; 
  PF_InvokeServiceFunc invokeService; 
} PF_PlatformServices;
Example 1

The version lets the plugin know the version of PluginManager it is hosted in. It lets the plugin make version-specific decisions like register different types of objects depending on the version of the host.

The registerObject() function is a PluginManager service through which the plugin registers its objects (without it there will be no plugin system).

The invokeService function is for application-specific services. The normal interaction between the application and the plugins (once the plugin objects have been created) is that the application invokes plugin object methods through the object model interfaces (e.g., IActor::play()). But, it is often desirable for a plugin to request some service from the application. It happens a lot when the application provides some managed execution environment where objects log progress, report errors or allocate memory in a centralized way. The application typically provides a standard logger, error reporting function, and memory allocator and all the objects use them. These service objects are often singletons or static functions and methods. Dynamic plugins can't access them directly. Unlike the registerObject service, the application-specific can't be defined in the generic PF_PlatformServices struct, because they are not known and to the generic plugin framework and they will be different for different applications. The application may wrap all these service access points and define a big struct that contains all of them and pass it to each plugin object through an object model interface method (e.g., initObject()) that every plugin object must implement. This is not always convenient in the presence of C plugins. The service objects are most likely implemented in C++ and accept and return C++ objects as arguments, maybe they are templated and maybe they throw exceptions. It is possible to provide a C compatible wrapper for each one. However, often it is easier to funnel all the service requests through a single sink that handles all the interaction with the plugins.

This is what invokeService() is all about. The signature is very simple -- a string for the service name and a void pointer to arbitrary struct. This is a weakly typed interface, but it provides absolute flexibility. The plugin and the application must coordinate the available services and what the params struct should be. Example 2 demonstrates a logging service that accepts a filename, line number, and message and logs.

typedef struct LogServiceParams
  const apr_byte_t * filename;
  apr_uint32_t       line;
  const apr_byte_t * message;
} LogServiceParams;

Some Application File...

#include "LogServiceParams.h"

apr_int32_t InvokeService(const apr_byte_t * serviceName, 
                          void * serviceParams)
  if (::strcmp(serviceName, "log") == 0)
	LogServiceParams * lsp = (LogServiceParams *)serviceParams;
	Logger::log(lsp->filename, lsp->line, lsp->message);
Example 2

The LogServiceParams struct is defined in a header file that both the plugin and the application #include. This provides the logging protocol between them. The plugin packs the current filename, line number and log message in the structs and calls the invokeService() function with "log" as the service name and a pointer to the struct (as a void *). The implementation of the invokeService() function on the application side gets the pointer to the struct as a void pointer casts it to LogServiceParams struct and then calls the Logger::log() method with the information. If the plugin doesn't send a proper LogServiceParams struct the behavior is undefined (but definitely bad). The invokeService() can be used to handle multiple service requests and can let the plugin know by returning -1 that it failed. If the application needs to return a result to the plugin, output variable can be added to the service params struct. Each service may have its own params struct.

For example if the application wants to control memory allocation because it uses a custom memory allocation scheme it can provide an "allocate" and "deallocate" service. Whenever the plugin needs to allocate memory instead of doing malloc or new on its own it will call the "allocate" service and the AllocateServiceParams struct will contain the requested size and an output void * (or char *) for the allocated buffer.

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.