
Hi, Jeremy Pack wrote:
It is, basically, a library for building plugins.
I've needed that myself a couple of times. My current approach looks like this: 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. Simon