From e5378de5f1ac4ae820b820f64e8201a79f68f525 Mon Sep 17 00:00:00 2001 From: vaczi Date: Tue, 25 May 2021 13:48:09 +0200 Subject: [PATCH 1/7] added iterator interface --- linktimeplugin.hpp | 59 +++++++++++++++++++++------------------------- main.cpp | 13 +++++++++- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/linktimeplugin.hpp b/linktimeplugin.hpp index 1601301..09b472c 100644 --- a/linktimeplugin.hpp +++ b/linktimeplugin.hpp @@ -1,14 +1,15 @@ /** * @brief Link-time plug-in management - * @version 1.0.0 + * @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) * @copyright MIT license */ #pragma once -#include #include /** @@ -25,6 +26,8 @@ * 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. + * 6. Alternatively iterate the RegistrarBase objects with begin()/end() iterators. + * Don't forget to downcast like (*it)() */ namespace linktimeplugin { /* @@ -36,17 +39,8 @@ namespace linktimeplugin { class RegistrarBase { public: // Ctor. Adds this object to the list of registrars. - RegistrarBase() noexcept try { - if (!registrars_) { - registrars_.reset(new std::vector*>); - } - registrars_->push_back(this); - } catch(...) { - // Probably out-of-memory or similar. - // There's nothing we can do, but we don't let the - // exception escape b/c we're initializing a global - // static object, and there's no other way to catch - // it. + RegistrarBase() noexcept { + registrars().push_back(this); } // Rule of 5 @@ -59,32 +53,33 @@ namespace linktimeplugin { // Implemented by the derived registrar class. virtual BASE& operator()() = 0; + typedef std::vector PluginColl; + typedef std::vector*> RegistarColl; + // Returns all registrars. - static std::vector plugins() { - std::vector ret; - - if (registrars_) { - for(auto r : *registrars_) { - ret.push_back(&(*r)()); - } + static PluginColl plugins() { + PluginColl ret; + for(auto r : registrars()) { + ret.push_back(&(*r)()); } - return ret; } - + // Use (*begin())() to get BASE object! + static auto begin() { + return registrars().begin(); + } + + static auto end() { + return registrars().end(); + } + private: - // Pointers to the registrar objects (one per registered - // plug-in class). - static std::unique_ptr*>>registrars_; + static RegistarColl& registrars() { + static RegistarColl regs; + return regs; + } }; - /* - * 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). diff --git a/main.cpp b/main.cpp index 843d263..b4abeaf 100644 --- a/main.cpp +++ b/main.cpp @@ -3,11 +3,14 @@ * @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 { @@ -77,6 +80,14 @@ int main() { for (const auto animal : linktimeplugin::plugins()) { std::cout << animal->name() << ": " << animal->sound() << '\n'; } - + std::cout << "again with iterators\n"; + std::for_each( + linktimeplugin::RegistrarBase::begin(), + linktimeplugin::RegistrarBase::end(), + [](const auto x) { + // Notice the downcast with function operator! + std::cout << (*x)().name() << ": " << (*x)().sound() << '\n'; + }); + return EXIT_SUCCESS; } From d79e63872dd0c314d0f9e5e63a0df39a646a6c7a Mon Sep 17 00:00:00 2001 From: vaczi Date: Tue, 25 May 2021 13:54:53 +0200 Subject: [PATCH 2/7] extended readme --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 63a738b..0734b65 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,13 @@ namespace { for (const auto plugin : linktimeplugin::plugins()) { plugin->dosomething(); } - +// OR +std::for_each( + linktimeplugin::RegistrarBase::begin(), + linktimeplugin::RegistrarBase::end(), + [](const auto x) { + (*x)().dosomething(); + }); ``` Complete example: `main.cpp`. To build and run the example program: From 3e21d382f9cc311dae80805cc215cfe04b867c9d Mon Sep 17 00:00:00 2001 From: Janos Vaczi <43462370+vaczijani@users.noreply.github.com> Date: Tue, 15 Jun 2021 17:28:16 +0200 Subject: [PATCH 3/7] Update linktimeplugin.hpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit typo in typedef Co-authored-by: Wolfram Rösler --- linktimeplugin.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linktimeplugin.hpp b/linktimeplugin.hpp index 09b472c..4e0150b 100644 --- a/linktimeplugin.hpp +++ b/linktimeplugin.hpp @@ -54,7 +54,7 @@ namespace linktimeplugin { virtual BASE& operator()() = 0; typedef std::vector PluginColl; - typedef std::vector*> RegistarColl; + typedef std::vector*> RegistrarColl; // Returns all registrars. static PluginColl plugins() { From dae41b688dc54d9c10552e0a1e3034c9f2cfa082 Mon Sep 17 00:00:00 2001 From: vaczijani Date: Wed, 16 Jun 2021 11:07:56 +0200 Subject: [PATCH 4/7] Simplified class hierarchy to get easier access to collection and iterator --- CMakeLists.txt | 16 +-- linktimeplugin.hpp | 243 +++++++++++++++++++-------------------------- main.cpp | 181 ++++++++++++++++----------------- 3 files changed, 200 insertions(+), 240 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bf38b0..a6321a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ -# Link-time plug-ins example program -# Build configuration -# by Wolfram Rösler 2018-06-25 - -cmake_minimum_required(VERSION 3.5) -set(CMAKE_CXX_STANDARD 17) -project(linktimeplugin) -add_executable(linktimeplugin main.cpp) +# Link-time plug-ins example program +# Build configuration +# by Wolfram Rösler 2018-06-25 + +cmake_minimum_required(VERSION 3.5) +set(CMAKE_CXX_STANDARD 11) +project(linktimeplugin) +add_executable(linktimeplugin main.cpp) diff --git a/linktimeplugin.hpp b/linktimeplugin.hpp index 09b472c..7da848a 100644 --- a/linktimeplugin.hpp +++ b/linktimeplugin.hpp @@ -1,139 +1,104 @@ -/** - * @brief Link-time plug-in management - * @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) - * @copyright MIT license - */ - -#pragma once - -#include - -/** - * Link-time plug-in management. - * - * 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 - * 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. - * 6. Alternatively iterate the RegistrarBase objects with begin()/end() iterators. - * Don't forget to downcast like (*it)() - */ -namespace linktimeplugin { - /* - * Base class for plug-in registrars. A registrar is an intermediate - * class that manages the registration of one plug-inclass (which - * is derived from the common plug-in base class). - */ - template - class RegistrarBase { - public: - // Ctor. Adds this object to the list of registrars. - RegistrarBase() noexcept { - registrars().push_back(this); - } - - // Rule of 5 - virtual ~RegistrarBase() = default; - 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; - - typedef std::vector PluginColl; - typedef std::vector*> RegistarColl; - - // Returns all registrars. - static PluginColl plugins() { - PluginColl ret; - for(auto r : registrars()) { - ret.push_back(&(*r)()); - } - return ret; - } - // Use (*begin())() to get BASE object! - static auto begin() { - return registrars().begin(); - } - - static auto end() { - return registrars().end(); - } - - private: - static RegistarColl& registrars() { - static RegistarColl regs; - return regs; - } - }; - - /* - * Derived registrar class. - * PLUGIN is the plug-in class (derived from the plug-in base class). - */ - template - class Registrar : public RegistrarBase { - PLUGIN plugin_; - - typename PLUGIN::Base& operator()() override { - return plugin_; - } - }; - - /** - * 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 +/** + * @brief Link-time plug-in management + * @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) + * @copyright MIT license + */ + +#pragma once + +#include + +/** + * Link-time plug-in management. + * + * 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 + * 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. + * 6. Alternatively iterate the RegistrarBase objects with begin()/end() iterators. + * Don't forget to downcast like (*it)() + */ +namespace linktimeplugin { + /* + * Base class for plug-in registrars. A registrar is an intermediate + * class that manages the registration of one plug-inclass (which + * is derived from the common plug-in base class). + */ + template + class RegistrarBase { + public: + // Ctor. Adds this object to the list of registrars. + RegistrarBase() noexcept try { + registerPlugin(this); + } catch (...) { + } + + // Rule of 5 + virtual ~RegistrarBase() = default; + RegistrarBase(const RegistrarBase&) = delete; + RegistrarBase(RegistrarBase&&) = delete; + void operator=(const RegistrarBase&) = delete; + void operator=(RegistrarBase&&) = delete; + + typedef std::vector Collection; + typedef typename Collection::const_iterator const_iterator; + + // provide only const data for user + static const_iterator begin() { return registrars().begin(); } + static const_iterator end() { return registrars().end(); } + + static const Collection& getPlugins() { return registrars(); } + + private: + void registerPlugin(RegistrarBase* reg) { + registrars().push_back(static_cast(reg)); + } + + static Collection& registrars() { + static Collection singleton; + return singleton; + } + }; +} + +/** + * 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 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 b4abeaf..21d05f3 100644 --- a/main.cpp +++ b/main.cpp @@ -1,93 +1,88 @@ -/** - * @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. - 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'; - } - std::cout << "again with iterators\n"; - std::for_each( - linktimeplugin::RegistrarBase::begin(), - linktimeplugin::RegistrarBase::end(), - [](const auto x) { - // Notice the downcast with function operator! - std::cout << (*x)().name() << ": " << (*x)().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() { + typedef linktimeplugin::RegistrarBase Plugins; + for (const auto animal : Plugins::getPlugins()) { + std::cout << animal->name() << ": " << animal->sound() << '\n'; + } + std::cout << "again with iterators\n"; + for (auto it = Plugins::begin(); it != Plugins::end(); ++it) { + // Notice the downcast with function operator! + std::cout << (*it)->name() << ": " << (*it)->sound() << '\n'; + }; + + return EXIT_SUCCESS; +} From 1f08153637c6940c889e44d4d5e2d2de5fb61909 Mon Sep 17 00:00:00 2001 From: vaczijani Date: Wed, 16 Jun 2021 12:06:21 +0200 Subject: [PATCH 5/7] reformatted, added comments --- linktimeplugin.hpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/linktimeplugin.hpp b/linktimeplugin.hpp index 10e2f2b..6316041 100644 --- a/linktimeplugin.hpp +++ b/linktimeplugin.hpp @@ -5,6 +5,7 @@ * @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 */ @@ -39,13 +40,14 @@ namespace linktimeplugin { class RegistrarBase { public: // Ctor. Adds this object to the list of registrars. - RegistrarBase() noexcept try { - registerPlugin(this); - } catch (...) { - } + RegistrarBase() noexcept + try { + 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; @@ -55,10 +57,17 @@ namespace linktimeplugin { typedef typename Collection::const_iterator const_iterator; // provide only const data for user - static const_iterator begin() { return registrars().begin(); } - static const_iterator end() { return registrars().end(); } + static const_iterator begin() { + return registrars().begin(); + } - static const Collection& getPlugins() { return registrars(); } + static const_iterator end() { + return registrars().end(); + } + + static const Collection& getPlugins() { + return registrars(); + } private: void registerPlugin(RegistrarBase* reg) { @@ -72,7 +81,7 @@ namespace linktimeplugin { }; } -// REGISTER_PLUGIN clearly indicates intent +// REGISTER_PLUGIN clearly indicates registration intent #define REGISTER_PLUGIN(x) static x x##Instance // DEFINE_PLUGIN_INTERFACE avoids repetition of plugin class name From b68cafa32487e4ed4c053048d88f1cde65029216 Mon Sep 17 00:00:00 2001 From: vaczijani Date: Wed, 16 Jun 2021 17:24:37 +0200 Subject: [PATCH 6/7] Fixed comments. Removed RegistrarBase dependency in application code. --- README.md | 11 +++++------ linktimeplugin.hpp | 18 +++++++++--------- main.cpp | 14 +++++++++----- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 0734b65..7b6d8da 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,15 +126,15 @@ namespace { } // In the application: -for (const auto plugin : linktimeplugin::plugins()) { +for (const auto plugin : PluginBase::getPlugins()) { plugin->dosomething(); } // OR std::for_each( - linktimeplugin::RegistrarBase::begin(), - linktimeplugin::RegistrarBase::end(), + PluginBase::begin(), + PluginBase::end(), [](const auto x) { - (*x)().dosomething(); + (*x)->dosomething(); }); ``` diff --git a/linktimeplugin.hpp b/linktimeplugin.hpp index 6316041..619b854 100644 --- a/linktimeplugin.hpp +++ b/linktimeplugin.hpp @@ -19,16 +19,16 @@ * 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 + * You can DEFINE_PLUGIN_INTERFACE(className) + * 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. - * 6. Alternatively iterate the RegistrarBase objects with begin()/end() iterators. - * Don't forget to downcast like (*it)() + * 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 { /* diff --git a/main.cpp b/main.cpp index 21d05f3..0cb7162 100644 --- a/main.cpp +++ b/main.cpp @@ -74,15 +74,19 @@ namespace { // to see any of the plug-in classes (Cat, Dog, ...), only // the plug-in base class (PluginBase). int main() { - typedef linktimeplugin::RegistrarBase Plugins; - for (const auto animal : Plugins::getPlugins()) { + for (const auto animal : PluginBase::getPlugins()) { std::cout << animal->name() << ": " << animal->sound() << '\n'; } std::cout << "again with iterators\n"; - for (auto it = Plugins::begin(); it != Plugins::end(); ++it) { - // Notice the downcast with function operator! + 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; } From dc3956c74ac9d920b861b153dc6136dee0c5c03e Mon Sep 17 00:00:00 2001 From: vaczijani Date: Wed, 16 Jun 2021 17:32:18 +0200 Subject: [PATCH 7/7] Fixed README std::for_each lambda. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7b6d8da..9152dba 100644 --- a/README.md +++ b/README.md @@ -133,8 +133,8 @@ for (const auto plugin : PluginBase::getPlugins()) { std::for_each( PluginBase::begin(), PluginBase::end(), - [](const auto x) { - (*x)->dosomething(); + [](PluginBase* x) { + x->dosomething(); }); ```