Last active
May 15, 2023 19:14
-
-
Save Cilyan/a8117124b04b64642646 to your computer and use it in GitHub Desktop.
Compile-Time Plugin System from https://codereview.stackexchange.com/questions/119812/compile-time-plugin-system
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include "plugins.h" | |
int main() | |
{ | |
auto &factory = PluginSystem::PluginFactory::Instance(); | |
auto plugin = factory.GetPlugin("Plugin1"); | |
plugin->DoSomething(); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Could be also split in .h/.cpp, here kept in one file for simplicity */ | |
#include <iostream> | |
#include "plugins.h" | |
class Plugin1: public PluginSystem::IPlugin { | |
void DoSomething() { | |
std::cout << "Plugin1" << std::endl; | |
} | |
}; | |
REGISTER_PLUGIN(Plugin1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "plugins.h" | |
namespace PluginSystem { | |
PluginFactory& | |
PluginFactory::Instance() { | |
static PluginFactory instance; | |
return instance; | |
} | |
void | |
PluginFactory::Register(IPluginRegistrar* registrar, std::string name) { | |
registry_[name] = registrar; | |
} | |
std::unique_ptr<IPlugin> | |
PluginFactory::GetPlugin(std::string name) { | |
/* throws out_of_range if plugin unknown */ | |
IPluginRegistrar* registrar = registry_.at(name); | |
return registrar->GetPlugin(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <list> | |
#include <string> | |
#include <map> | |
#include <memory> | |
namespace PluginSystem { | |
/* Base class for plugins */ | |
class IPlugin { | |
public: | |
virtual void DoSomething() = 0; | |
}; | |
/* | |
* Base class for PluginRegistrar | |
* See PluginRegistrar below for explanations | |
*/ | |
class IPluginRegistrar { | |
public: | |
virtual std::unique_ptr<IPlugin> GetPlugin() = 0; | |
}; | |
/* | |
* This is the factory, the common interface to "plugins". | |
* Plugins registers themselves here and the factory can serve them on | |
* demand. | |
* It is a Singleton | |
*/ | |
class PluginFactory { | |
public: | |
/* Get Singleton instance */ | |
static PluginFactory& Instance(); | |
/* Register a new plugin */ | |
void Register(IPluginRegistrar* registrar, std::string name); | |
/* Get an instance of a plugin based on its name */ | |
/* throws out_of_range if plugin not found */ | |
std::unique_ptr<IPlugin> GetPlugin(std::string name); | |
private: | |
/* Holds pointers to plugin registrars */ | |
std::map<std::string, IPluginRegistrar*> registry_; | |
/* Make constructors private and forbid cloning */ | |
PluginFactory(): registry_() {}; | |
PluginFactory(PluginFactory const&) = delete; | |
void operator=(PluginFactory const&) = delete; | |
}; | |
/* | |
* Helper class that registers a plugin upon construction. | |
* Actually, the registrar registers itself, and the proxied plugin is only | |
* created on-demand. This mechanism can be shortened by directly | |
* registering and instance of the plugin, but the assumption here is that | |
* instanciating the plugin can be heavy and not necessary. | |
*/ | |
template<class TPlugin> | |
class PluginRegistrar: public IPluginRegistrar { | |
public: | |
PluginRegistrar(std::string classname); | |
std::unique_ptr<IPlugin> GetPlugin(); | |
private: | |
/* That is not really used there, but could be useful */ | |
std::string classname_; | |
}; | |
/* template functions in header */ | |
template<class TPlugin> | |
PluginRegistrar<TPlugin>::PluginRegistrar(std::string classname): classname_(classname) { | |
PluginFactory &factory = PluginFactory::Instance(); | |
factory.Register(this, classname); | |
} | |
template<class TPlugin> | |
std::unique_ptr<IPlugin> | |
PluginRegistrar<TPlugin>::GetPlugin() { | |
std::unique_ptr<IPlugin> plugin(new TPlugin()); | |
return plugin; | |
} | |
} | |
/* | |
* Here is the trick: upon creation of the global variable, the class created | |
* out of the template will get instanciated once, and will register itself. | |
* The template contains the information to create a plugin instance. | |
* An unnamed namespace is used to enclose this later unused variable in the | |
* compilation unit. | |
*/ | |
#define REGISTER_PLUGIN(CLASSNAME) \ | |
namespace { \ | |
static PluginSystem::PluginRegistrar<CLASSNAME> \ | |
_registrar( #CLASSNAME ); \ | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment