PluginHelper
The PluginHelper is yet another helper class that takes the drudge out of writing the plugin glue code. Listing Three is the code.
#ifndef PF_PLUGIN_HELPER_H #define PF_PLUGIN_HELPER_H #include "plugin.h" #include "base.h" class PluginHelper { struct RegisterParams : public PF_RegisterParams { RegisterParams(PF_PluginAPI_Version v, PF_CreateFunc cf, PF_DestroyFunc df, PF_ProgrammingLanguage pl) { version=v; createFunc=cf; destroyFunc=df; programmingLanguage=pl; } }; public: PluginHelper(const PF_PlatformServices * params) : params_(params), result_(exitPlugin) { } PF_ExitFunc getResult() { return result_; } template <typename T> void registerObject(const apr_byte_t * objectType, PF_ProgrammingLanguage pl=PF_ProgrammingLanguage_C, PF_PluginAPI_Version v = {1, 0}) { RegisterParams rp(v, T::create, T::destroy, pl); apr_int32_t rc = params_->registerObject(objectType, &rp); if (rc < 0) { result_ = NULL; THROW << "Registration of object type " << objectType << "failed. " << "Error code=" << rc; } } private: static apr_int32_t exitPlugin() { return 0; } private: const PF_PlatformServices * params_; PF_ExitFunc result_; }; #endif // PF_PLUGIN_HELPER_H
It is designed to work with plugin object classes that implement the PF_CreateFunc and PF_DestroyFunc mandatory functions as static methods. That's it. No other requirements. As it happens ActorBaseTemplate satisfies this requirement so plugin object classes that derive from ActorBaseTemplate are automatically compatible with PluginHelper. The PluginHelper is designed to be used inside the mandatory PF_initPlugin() entry point. You will see it in action in the next article when I cover writing plugins. For now, I'll just go over the services PluginHelper makes available to the plugin developer. The job of the entry point function is to register all the plugin object types supported by the plugin and if successful return a function pointer to a PF_ExitFunc exit function with a particular signature. If something goes wrong it should return NULL.
The PluginHelper constructor accepts a pointer to the PF_PlatfromServices struct that contains the host system plugin API version and invokeService and registerObject function pointers and stores them. It also stores in its result member the exitPlugin function pointer that will be returned if the plugin initialization is successful.
PluginHelper provides the templated registerObject method that does most of the work. The T template parameter is the object type that you want to register. It should have a create() and destroy() static methods that conform to PF_CreateFunc and PF_DestroyFunc. It accepts an object type string and optional programming language (defaults to PF_ProgrammingLanguage_C). This method performs a version check to make sure the plugin version is compatible with the host system. If everything is fine it prepares a RegisterObjectParams struct and calls the registerObject() function and check the result. If the version check or the invocation of the registerObject function pointer fail it will report the error (this is done by the CHECK macro if the condition is false), set the result_ to NULL and swallow the exception thrown by CHECK. The reason it doesn't let the exception propagate is because PF_initPlugin (where PluginHelper is supposed to be used) is a C function that shoul not let exceptions propagate across the binary compatibility bounday. Catching all exceptions in registerObject saves the plugin developer the trouble doing it (or worse, forgetting to do it). This is a fine example of the convenience of using the THROW, CHECK, and ASSERT macros. The error message is constructed easily using the streaming operator. No need to allocate buffers, concatanate strings or use printf. The resulting reportError call will contain the exact location of the error (__FILE__, __LINE__) without having to explicitly specify it.
Typically, a plugin will register more than one object type. If any object type fails to register the result_ will be NULL. It may be okay for some object types to fail registration. For example, you may register multiple versions of the same object type and one of the versions is not supported anymore by the host system. In this case only this object type will fail to register. The plugin developer may check the value of result_ after each call to PluginHelper::registerObject() and decide if it's fatal or not. If it's a benign failure it may eventually return PluginHelper::ExitPlugin after all.
The default behavior is that every failure is fatal and the plugin developer should just return PluginHelper::getResult() that will return the value of result_, which will be PluginHelper::ExitPlugin (if all registrations succeeded) or NULL (if any registration failed).