Parallel

Building Your Own Plugin Framework: Part 4

By Gigi Sayfan, January 14, 2008

Hybrid C/C++ plugins

ActorBaseTemplate

ActorBaseTemplate is the heart of the hybrid approach. The plugin developer just has to derive from it and implement the C++ IActor interface and automatically the plugin will communicate with the plugin manager via the C interface and provide full binary compatibility. The pluguin developer should never see the C interface or even be aware of it.

This template provides many services to its sub-classes so let's take it slowly. Example 1 contains the declaration of the template.

There are two template parameters: T and Interface. T is type of the subclass and when you derive from ActorBaseTemplate you must provide the type of the derived class to the base class (template). This is an instance of CRTP, the "Curiously Recurring Template Pattern". Interface is the interface that the plugin object will use to communicate with the plugin manager. It can be the C++ IActor or the C C_Actor. By default it is C_Actor. You may wonder why is not always C_Actor. After all if the plugin object wishes to communicate with the plugin manager using C++ it can just register itself as a C++ object and directly derive from IActor. This is good thinking. The reason AutoBaseTemplate supports IActor too, is to let you switch effortlessly from C to C++ interfaces. This is useful during debugging when you want to skip the whole C wrapper code and also if you want to deploy in a controlled environment and you don't need the full C compatibility. In this case, with a flip of a template parameter and you change the underlying communication channel.

ActorBaseTemplate derives from both C_Actor and IActor. It even provides a trivial implementation of IActor in case you want to implement only part of the interface. That saves you from declaring empty methods yourself. The C_Actor is the critical interface because this is the interface used to communicate with the plugin manager when Interface=C_Actor.

It accepts no arguments initializes the invokeService_ function pointer to NULL and goes on to initialize the members of its C_Actor interface to point to static functions and the assigns the this pointer to the handle. This is similar to the C/C++ dual object model and indeed it is a dual object, except that the actual C++ implementation that does the real work is in the derived class.

Example 3 is the mandatory PF_CreateFunc and PF_DestroyFunc that are registered with the plugin manager and are invoked to create and destroy instances.

They are named create() and destroy() but the names are irrelevant because they are registered and invoked as function pointer and not by name. The fact that ActorBaseTemplate defines them saves a lot of headaches to aspiring plugin developers. The create() function simply creates a new instance of T (the derived class) and initalizes assigns the invokeService function pointer to the invokeService_ data member. The destroy() function casts the void pointer it gets to the Interface template arguments and then use the getSelf() method (discussed shortly) to get a properly typed pointer to the T derived class. It subsequently calls delete to destroy the instance for good. This is really nice. The plugin developer creates a simple C++ class with a standard constructor (that accepts PF_ObjectParams, but it can ignore it) and destructor and the ActorBaseTemplate does its magic under the covers and make sure that all the weird static functions will be routed properly to derived class.

There are three overloads for IActor, C_Actor, and C_ActorHandle. The getSelf() method just performs a static_cast to get from the interface to full dual object as you have seen before. In the case of the handle it just performs a C cast to make it a C_Actor. As you saw in the constructor and later again the ActorBaseTemplate often gets an Interface or handle when it really needs itself to keep going.

This is a convenience function that forwards the call to the invokeService function pointer. It saves the caller from packing its arguments into the ReportErrorParams defined by the application's services.h header and from invoking the service with the "reportError" string. These error-reporting conventions are defined by the application service layer and are immaterial to the plugin developer who just wants to churn out plugin objects as fast and easy as possible.

The implementation of both interface functions is almost identical: getSelf(), calls the C++ IActor implementation in the derived class via the wonders of polymorphism and employs robust error handling. Before I discuss the error handling, pay attention to staticPlay() function. It accepts a C_Turn interface, wraps it in a TurnWrapper, and then passes it to the IActor::play() method where it will arrive as a C++ ITurn. This is what the wrappers are for.

The error handling is another nice feature of ActorBaseTemplate. It lets plugin developers forget that they are writing a plugin object that must adhere to strict rules (such as not throwing exceptions across the binary compatibility boundary) and just throw exceptions on error. Every call to the derived class (except for the constructor and destructor) is wrapped in these try-except clauses. There is here a chain of exception handler from the most informative to the least informative. The plugin developer may elect to throw the StreamingException class defined by plugin framework. This is a nice standalone exception class that contains the location (filename and line number) of thrown exception in addition to an error message. If you want to learn more about StreamingException, see Practical C++ Error Handling in Hybrid Environments.

Listing Two contains a few convenient macros for checking and asserting that throw StreamingException on failure.

This is nice for debugging purposes because you the end result is all this information will propagate to the application invokeService() implementation via the reportError() method. If the plugin developer chooses to throw a standard std::runtime_error, then the error-handling code extracts the error message from the what() method, but no meaningful filename and line number will be provided. The __FILE__ and __LINE__ macros will report the file and line number of the error-handling code in ActorBaseTemplate and not the actual location of the error. Finally, the fallback is catching any exception with the elipsis except handler. Here, there isn't even an error message to extract and a generic message that at least records the name of the failed function is provided.

The bottom line is that ActorBaseTemplate frees the plugin developer from all the vagaries of implementing a plugin object and allows the developer concentrate on implementing the object interface in standard C++ (IActor in this case) without getting tangled up with strange requirements like defining special static methods for creation and destruction, reporting error through funny function pointers, or dealing with any shred of C.

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.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!