From: Simon Richter (Simon.Richter_at_[hidden])
Date: 2006-10-07 19:45:01
Jeremy Pack wrote:
> It is, basically, a library for building plugins.
I've needed that myself a couple of times. My current approach looks
There is a base class, "registrable", that provides type_info and a
vtable. Since it is the easiest way to achieve that, I just gave it a
virtual destructor. This class serves as the base for all objects
handled through the library.
There is a template class "registry", whose parameter suggests the type
of object this registry manages. registry<registrable> is specialized;
it descends from registrable to denote that registries themselves can be
registered, and defines abstract methods for registering a "registrable"
object with itself. registry<...> is descended from
registry<registrable> and implements the abstract method (as a
dynamic_cast (if the cast fails, the function returns and does nothing)
and handover to the function taking the concrete type), plus it manages
a list of objects of the respective type (or rather, two, due to
constness; this is pretty icky, but I haven't found a better solution yet).
There is also a class deriving from registry<registrable> that just
implements the abstract functions and keeps a list. This class is
instantiated once as a global object; also, one instance of
registry<registry<registrable> > is instantiated. These are not
registered with each other, but that is the only exception.
So, now every registry can accept every "registrable" object and will
ignore it when the types do not match.
Now there are global functions that perform global registration of
objects. Now, we walk the list of registries, passing the object to each
in turn to see whether it can do something with it, and, if the new
object is itself a registry, we pass all objects registered so far to
it; after that, we add the new object to the list of objects and the
list of registries (if it is a registry; it is safe but pointless to add
it to the registry of registries if it is not a registry, since it would
just be ignored).
So much for the boring part.
Now, my factory classes are descended from registry<creator<T> >, and of
course registered in the registry of registries. So, any creator<T>
object that happens to be registered will automatically be made
available to all interested factories; also, since the factory is
ultimately descended from registrable, when a factory is registered, it
will be presented with all objects so far, and just needs to pick out
the creator<T> objects.
Now, my plugin loader is fairly straightforward: there is an (abstract)
loader class that is to be derived from when implementing a plugin
loader; an abstract plugin_impl class that is returned by the plugin
loader when loading something, and an abstract plugin class wrapping a
pointer to plugin_impl. The purpose of the indirection is so that the
application author can derive a concrete application specific plugin
class; as a special courtesy, there is also a class named
"registered_plugin" that looks up the symbol "plugin_info" and calls
that; the return value is a pointer to a struct that has a few
informational fields (name, version) and two lists (const and nonconst
"registrable" objects to be registered).
So, as an application programmer, you'd get a platform_plugin_loader
object (typedef'd in a header file) and ask it to load a few
"registered_plugin"s; the rest is sorted out automatically as long as
your factories are properly registered.
> I'm slogging through one of the tough areas (interdependancies between
> loadable objects that are not known when the original program is compiled),
> after which I'll post a link to the code and some examples and a little bit
> of documentation for anyone to look at.
I'm not really handling interdependencies; basically, I'm assuming that
the operating system will not be able to resolve symbols between
plugins, so I might as well ask for all of them to be self-contained.
The system is designed so that load order does not matter; objects noone
knows are just ignored until some other plugin is loaded that knows
them. This could be extended to detect this case, probably.
Now, where is the code? Being refactored, of course. :-) I might be able
to get something publishable soon; a lot of this code still has
CamelCase class names and other Javaisms, and does not necessarily fit
together ATM (since I reworked the registrable/registry bit already, but
not the rest), so it's not suitable for release yet.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk