diff --git a/CMakeLists.txt b/CMakeLists.txt index bc7a64f..0f15f26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,4 +5,4 @@ cmake_minimum_required(VERSION 3.5) set(CMAKE_CXX_STANDARD 11) project(linktimeplugin) -add_executable(linktimeplugin main.cpp) +add_executable(linktimeplugin main.cpp) \ No newline at end of file diff --git a/README.md b/README.md index 63a738b..9152dba 100644 --- a/README.md +++ b/README.md @@ -112,9 +112,8 @@ Overview: #include // In a header file: -class PluginBase { +class PluginBase : public RegistrarBase { public: - using Base = PluginBase; virtual void dosomething() = 0; }; @@ -127,10 +126,16 @@ namespace { } // In the application: -for (const auto plugin : linktimeplugin::plugins()) { +for (const auto plugin : PluginBase::getPlugins()) { plugin->dosomething(); } - +// OR +std::for_each( + PluginBase::begin(), + PluginBase::end(), + [](PluginBase* x) { + x->dosomething(); + }); ``` Complete example: `main.cpp`. To build and run the example program: diff --git a/linktimeplugin.hpp b/linktimeplugin.hpp index dff9bf8..e98f6bb 100644 --- a/linktimeplugin.hpp +++ b/linktimeplugin.hpp @@ -3,12 +3,14 @@ * @version 1.0.1 * @author Wolfram Rösler * @date 2018-06-25 + * change: + * 2021-05-25: registrars vector to static and added iterator interface (janos.vaczi@gmail.com) + * 2021-06-16: Removed intermediate class Registrar and doing the downcasting in RegistrarBase * @copyright MIT license */ #pragma once -#include #include /** @@ -16,15 +18,17 @@ * * Usage: * - * 1. Define a base class for your plug-ins. - * 2. In this plug-in base class, add "public: using Base=x", - * where x is the name of the plug-in base class. - * 3. For every plug-in, derive a class from the base class. - * 4. For every such class, invoke REGISTER_PLUGIN(x), where x is the + * 1. Define an abstract base class for your plug-ins as an interface, + * derived from RegistrarBase. You can use DEFINE_PLUGIN_INTERFACE(YourPluginInterfaceName) + * 2. For every plug-in, derive a class from the base class. + * 3. For every such class, invoke REGISTER_PLUGIN(x), where x is the * name of the derived plug-in class. - * 5. To retrieve a list of all plug-ins, invoke linktimeplugin::plugins(), - * where x is the name of the plug-in base class. This function - * returns a pointer to an instance of every plug-in class. + * 4. To retrieve a list of all plug-ins, invoke + * x::getPlugins(), where x is the name of the plug-in base class. + * This function returns a collection of pointer to an instance of + * every plug-in class. + * 5. Alternatively iterate the plug-in objects with + * x::begin() and x::end() iterators. */ namespace linktimeplugin { /* @@ -32,109 +36,53 @@ namespace linktimeplugin { * class that manages the registration of one plug-in class (which * is derived from the common plug-in base class). */ - template + template class RegistrarBase { public: // Ctor. Adds this object to the list of registrars. - RegistrarBase() noexcept { + RegistrarBase() noexcept try { - if (!registrars_) { - registrars_.reset(new std::vector*>); - } - registrars_->push_back(this); - } catch(...) {} - } + registerPlugin(this); + } catch (...) {} // Rule of 5 virtual ~RegistrarBase() = default; + // non-copiable and non-moveable RegistrarBase(const RegistrarBase&) = delete; RegistrarBase(RegistrarBase&&) = delete; void operator=(const RegistrarBase&) = delete; void operator=(RegistrarBase&&) = delete; - // Implemented by the derived registrar class. - virtual BASE& operator()() = 0; - - // Returns all registrars. - static std::vector plugins() { - std::vector ret; + typedef std::vector Collection; + typedef typename Collection::const_iterator const_iterator; + + // provide only const data for user + static const_iterator begin() { + return registrars().begin(); + } - if (registrars_) { - for(auto r : *registrars_) { - ret.push_back(&(*r)()); - } - } + static const_iterator end() { + return registrars().end(); + } - return ret; + static const Collection& getPlugins() { + return registrars(); } private: - // Pointers to the registrar objects (one per registered - // plug-in class). - static std::unique_ptr*>> registrars_; - }; - - /* - * Static member of the registrar base class. - * BASE is the plug-in base class. - */ - template - std::unique_ptr*>> RegistrarBase::registrars_; - - /* - * Derived registrar class. - * PLUGIN is the plug-in class (derived from the plug-in base class). - */ - template - class Registrar : public RegistrarBase { - PLUGIN plugin_; + void registerPlugin(RegistrarBase* reg) { + registrars().push_back(static_cast(reg)); + } - typename PLUGIN::Base& operator()() override { - return plugin_; + static Collection& registrars() { + static Collection singleton; + return singleton; } }; - - /** - * Get pointers to instances of all registered plug-in classes. - * - * T is the plug-in base class. - * - * Example: (MyBase is the plug-in base class, DoSomething is a pure - * virtual function in the plug-in base class, implemented by the - * derived plug-in classes) - * - * for (auto& p : linktimeplugin::plugins()) { - * p->DoSomething(); - * } - */ - template - std::vector plugins() { - return RegistrarBase::plugins(); - } } -/** - * Register one plug-in class. - * Use this once for every plug-in class that's derived from the - * plug-in base class. - * - * x is the name of the derived plug-in class. - * - * Example: - * - * // Base class - * class PluginBase { - * public: - * using Base = PluginBase; - * virtual void DoSomething() = 0; - * }; - * - * // Plug-in class - * class Plugin: public PluginBase { - * void DoSomething() override { ... } - * }; - * - * // Register the plug-in class - * REGISTER_PLUGIN(Plugin); - */ -#define REGISTER_PLUGIN(x) static linktimeplugin::Registrar x##registrar +// REGISTER_PLUGIN clearly indicates registration intent +#define REGISTER_PLUGIN(x) static x x##Instance + +// DEFINE_PLUGIN_INTERFACE avoids repetition of plugin class name +#define DEFINE_PLUGIN_INTERFACE(x) class x : public linktimeplugin::RegistrarBase diff --git a/main.cpp b/main.cpp index 843d263..0cb7162 100644 --- a/main.cpp +++ b/main.cpp @@ -1,82 +1,92 @@ -/** - * @brief Link-time plug-ins demo program - * @version 1.0.0 - * @author Wolfram Rösler - * @date 2018-06-25 - * @copyright MIT license - */ - -#include -#include -#include "linktimeplugin.hpp" - -namespace { - // Define the plug-in base class. - // In a real application, this would be in a header file. - class PluginBase { - public: - // Make the class known to the registrar - using Base = PluginBase; - - // Define the functions that are to be implemented by - // the plug-ins. Must be pure virtual and public in the - // base class. Can have any name and signature. - virtual std::string name() = 0; - virtual std::string sound() = 0; - }; - - // Define the first plug-in. - // In a real application, this would be in its own .cpp - // file, within an anonymous namespace (so the plug-in - // isn't visible anywhere outside its own file). - // Implements all plug-in functions (but the implementation - // can be private in the plug-in class). - class Cat : public PluginBase { - std::string name() override { - return "Cat"; - } - - std::string sound() override { - return "Meow"; - } - }; - REGISTER_PLUGIN(Cat); - - // Define the second plug-in. - // Again, in a real application this would be in an - // anonymous namespace within its own .cpp file. - class Dog : public PluginBase { - std::string name() override { - return "Dog"; - } - - std::string sound() override { - return "Woof"; - } - }; - REGISTER_PLUGIN(Dog); - - // Define the third plug-in. - class Bird : public PluginBase { - std::string name() override { - return "Bird"; - } - - std::string sound() override { - return "Tweet"; - } - }; - REGISTER_PLUGIN(Bird); -} - -// Main function. -// Note that, in a real application, this will not be able -// to see any of the plug-in classes (Cat, Dog, ...), only -// the plug-in base class (PluginBase). -int main() { - for (const auto animal : linktimeplugin::plugins()) { - std::cout << animal->name() << ": " << animal->sound() << '\n'; - } - - return EXIT_SUCCESS; -} +/** + * @brief Link-time plug-ins demo program + * @version 1.0.0 + * @author Wolfram Rösler + * @date 2018-06-25 + * change: + * 2021-05-25: exercise iterator interface (janos.vaczi@gmail.com) + * @copyright MIT license + */ + +#include +#include +#include +#include "linktimeplugin.hpp" + +namespace { + // Define the plug-in base class. + // In a real application, this would be in a header file. + DEFINE_PLUGIN_INTERFACE(PluginBase) { + public: + // Define the functions that are to be implemented by + // the plug-ins. Must be pure virtual and public in the + // base class. Can have any name and signature. + virtual std::string name() = 0; + virtual std::string sound() = 0; + }; + + // Define the first plug-in. + // In a real application, this would be in its own .cpp + // file, within an anonymous namespace (so the plug-in + // isn't visible anywhere outside its own file). + // Implements all plug-in functions (but the implementation + // can be private in the plug-in class). + class Cat : public PluginBase { + std::string name() override { + return "Cat"; + } + + std::string sound() override { + return "Meow"; + } + }; + REGISTER_PLUGIN(Cat); + + // Define the second plug-in. + // Again, in a real application this would be in an + // anonymous namespace within its own .cpp file. + class Dog : public PluginBase { + std::string name() override { + return "Dog"; + } + + std::string sound() override { + return "Woof"; + } + }; + REGISTER_PLUGIN(Dog); + + // Define the third plug-in. + class Bird : public PluginBase { + std::string name() override { + return "Bird"; + } + + std::string sound() override { + return "Tweet"; + } + }; + REGISTER_PLUGIN(Bird); +} + +// Main function. +// Note that, in a real application, this will not be able +// to see any of the plug-in classes (Cat, Dog, ...), only +// the plug-in base class (PluginBase). +int main() { + for (const auto animal : PluginBase::getPlugins()) { + std::cout << animal->name() << ": " << animal->sound() << '\n'; + } + std::cout << "again with iterators\n"; + for (auto it = PluginBase::begin(); it != PluginBase::end(); ++it) { + std::cout << (*it)->name() << ": " << (*it)->sound() << '\n'; + }; + std::cout << "again with for_each\n"; + std::for_each(PluginBase::begin(), PluginBase::end(), + [](PluginBase* x) { + std::cout << x->name() << ": " << x->sound() << '\n'; + } + ); + + return EXIT_SUCCESS; +}