0

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.

mike
  • 1,192
  • 9
  • 32
  • 2
    Create a [mcve] – eerorika Feb 22 '21 at 13:01
  • 2
    Does `IPlugin` have any relationship to `IRenderer`? – Cory Kramer Feb 22 '21 at 13:05
  • 1
    The incomplete, non-working, code is too ... ummm ... incomplete for anyone to help. What is `IPlugin` and what is its relationship to the `T` for which the templated `PluginRegistrar` is specialised/instantiated? What about code that uses the templated classes/functions? – Peter Feb 22 '21 at 13:07
  • Sorry all, thought it would be more glaring than requiring full code. Should be everything up there now. – mike Feb 22 '21 at 14:50
  • 2
    I think `auto plugin= factory.GetPlugin("TestPlugin");` is supposed to be `auto& plugin= factory.GetPlugin("TestPlugin");` (make it a reference) – Kevin Feb 22 '21 at 15:10
  • @Kevin that was it, duh! Thanks a million! – mike Feb 22 '21 at 15:12
  • @mike I made it an answer – Kevin Feb 22 '21 at 15:25

1 Answers1

2
auto plugin= factory.GetPlugin("TestPlugin");

This will copy the result into a new object, which will be a IPlugin (the original derived type will be sliced). You want to use a reference instead:

auto& plugin= factory.GetPlugin("TestPlugin");

You might want to make IPlugin::DoThing() a pure virtual function (i.e. virtual void DoThing() = 0) which will

  1. Force derived classes to implement it
  2. Prevent you from having an object of type IPlugin, i.e. make your original code fail to compile
Kevin
  • 6,993
  • 1
  • 15
  • 24