I have a compile time "plugin" system based on this link, which I want to retain ownership of the plugins, which are literally unique - there should only be one instance of each type.
There is a registrar class which owns a plugin type, and owns a unique_ptr to the plugin.
I want to return a reference to the plugin whenever one is requested, to keep ownership clear.
More complete code below. Note the #define for registration in the header of a plugin.
Also, I'm renaming from Renderer in my code to Plugin here so if I've missed any that's what the random Render references are/were.
Factory class:
class PluginFactory
{
public:
// Instance of Factory
static PluginFactory& Instance();
void Register(IPluginRegistrar *Registrar, std::string name);
IPlugin& GetPlugin(std::string name);
private:
std::unordered_map<std::string, IPluginRegistrar *>_registry;
// private constructor, prevent copying
PluginFactory() : _registry() {};
PluginFactory(PluginFactoryconst&) = delete;
void operator=(PluginFactoryconst&) = delete;
};
PluginFactory& PluginFactory::Instance()
{
static PluginFactoryinstance;
return instance;
}
void PluginFactory::Register(IPluginRegistrar *registrar, std::string name)
{
if (_registry.count(name))
std::cout << name << " is already registered!\n";
else
{
std::cout << "Registering " << name << "\n";
_registry[name] = registrar;
}
}
IPlugin& PluginFactory::GetPlugin(std::string name)
{
IPluginRegistrar* registrar = _registry.at(name);
return registrar->GetPlugin();
}
#define REGISTER_PLUGIN(CLASSNAME) \
namespace { \
static MyNamespace::PluginRegistrar<CLASSNAME>\
_registrar( #CLASSNAME ); \
};
Plugin Base
class IPlugin
{
public:
IPlugin() { std::cout << "Constructing IPlugin class\n"; }
virtual void DoThing(){ std::cout << "Here's the thing from an IPlugin!\n"; };
};
Plugin Derived
class TestPlugin : public IPlugin
{
public:
TestPlugin ();
virtual void DoThing() override;
};
REGISTER_RENDERER(TestPlugin)
TestPlugin::TestPlugin () : IPlugin()
{
std::cout << "Constructing TestPlugin class\n";
}
void TestPlugin::DoThing()
{
std::cout << "Here's the thing from a TestPlugin!\n";
}
Registrar
class IPluginRegistrar
{
public:
virtual IPlugin& GetPlugin() = 0;
};
template <class T>
class PluginRegistrar: public IPluginRegistrar
{
public:
PluginRegistrar(std::string classname); // constructor registers plugin with a plugin factory
IPlugin& GetPlugin(); // GetPlugin is called by the factory
private:
std::string _name;
std::unique_ptr<IPlugin> _ptr;
};
template <class T>
PluginRegistrar<T>::PluginRegistrar(std::string classname) : _name(classname)
{
PluginFactory& factory = PluginFactory::Instance();
factory.Register(this, classname);
}
template <class T>
IPlugin &
PluginRegistrar<T>::GetPlugin()
{
if (!_ptr)
_ptr = std::make_unique<T>();
return *_ptr;
}
Code in use:
auto& factory = PluginFactory::Instance();
auto plugin= factory.GetPlugin("TestPlugin");
=> outputs confirm derived class constructed
plugin.DoThing();
=> output of IPlugin.DoThing(), not TestPlugin.DoThing()
The original version of this code returned a new unique_ptr<IPlugin>
each time GetPlugin()
was called, and worked great. However now I've switched return type to an IPlugin &
, when I call a virtual function on the result, the base class function is called, not the derived.