I tend to turn this entire thing around for flexibility reasons. The problem I've had with plugins which attempt to expose data to the system is that the structure/flags/whatever you wish to expose ends up constantly changing as you add more features and it makes keeping all the plugins up to date difficult. Instead of doing this, I let the plugins do the work themselves. Basically I load up the plugin and call a register function in the plugin. Now it is up to the plugin to find factories to hook into, tell systems about themselves etc. In this way, if I add a new factory I don't need to go through all the existing plugins and update the registration structures, existing plugins simply continue to work since they don't know about the new thing or need to change how they register. When I add a plugin for the new item, it just "works" because it is querying the system for the new factory to plug into instead of the system querying the plugin to figure out where it should be hooked.
Just a quick outline of this. (Note that I use a custom variation of the COM pattern, it is not MS COM though, just the pattern.)
for (auto plugin : foundPlugins)
plugin->Load(registry);
void Plugin::Load(Registry& registry) // plugins don't cause load failures, they just don't register and that is logged.
{
m_SharedObject.Load (m_Filename);
if (m_SharedObject.Loaded())
{
InstallFunction install = m_SharedObject.GetFunction<InstallFunction>("Install");
if (install)
{
if ((install)(registry))
return true;
}
}
// TODO: Clean up everything from above on failure...
}
// From a plugin.
bool stdcall Install(Registry& registry)
{
// I'm a file format plugin...
iFileFormatFactory* factory = nullptr;
if (registry.Create<iFileFormatFactory>(&factory)) // Note: the factory is probably a singleton internally, it deals with that behind the scenes.
{
if (factory->Install(MyPlugin::kId, &MyPlugin::Creator))
{
// Note: you can pass in descriptive flags above in a "real" system.
factory->Release();
return true;
}
}
return false;
}
There are a whole slew of benefits to this solution. For instance, if this plugin absolutely needs another plugin to exist before it can load, it could soft fail and add itself to a deferred load (i.e. try again) list in the registry to be called again after other things get a chance to load. Or, if this was itself the factory mentioned, it could register the factory, then implement a subdirectory scan to load plugins for itself.
Overall, this pattern has served me very well and allows for very intricate plugin systems without the primary "Registry" getting all bloated with options and parsing systems.