-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Iterator interface #1
Open
vaczijani
wants to merge
10
commits into
devolo:master
Choose a base branch
from
vaczijani:iterator-interface
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
e5378de
added iterator interface
d79e638
extended readme
3e21d38
Update linktimeplugin.hpp
vaczijani dae41b6
Simplified class hierarchy to get easier access to collection and ite…
3baaef4
simplify class hierarchy
1f08153
reformatted, added comments
c5e74a0
Merge branch 'master' into iterator-interface
vaczijani b68cafa
Fixed comments. Removed RegistrarBase dependency in application code.
2e35a16
merged with remote comment changes
dc3956c
Fixed README std::for_each lambda.
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,138 +3,86 @@ | |
* @version 1.0.1 | ||
* @author Wolfram Rösler | ||
* @date 2018-06-25 | ||
* change: | ||
* 2021-05-25: registrars vector to static and added iterator interface ([email protected]) | ||
* 2021-06-16: Removed intermediate class Registrar and doing the downcasting in RegistrarBase | ||
* @copyright MIT license | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <memory> | ||
#include <vector> | ||
|
||
/** | ||
* 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 | ||
* 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<x>(), | ||
* 4. To retrieve a list of all plug-ins, invoke | ||
* linktimeplugin::RegistrarBase<x>::getPlugins(), | ||
* where x is the name of the plug-in base class. This function | ||
* returns a pointer to an instance of every plug-in class. | ||
* 5. Altenatively use linktimeplugin::RegistrarBase<x>::begin() and | ||
* linktimeplugin::RegistrarBase<x>::end() iterators. | ||
*/ | ||
namespace linktimeplugin { | ||
/* | ||
* Base class for plug-in registrars. A registrar is an intermediate | ||
* class that manages the registration of one plug-in class (which | ||
* is derived from the common plug-in base class). | ||
*/ | ||
template<typename BASE> | ||
template<typename Plugin> | ||
class RegistrarBase { | ||
public: | ||
// Ctor. Adds this object to the list of registrars. | ||
RegistrarBase() noexcept { | ||
RegistrarBase() noexcept | ||
try { | ||
if (!registrars_) { | ||
registrars_.reset(new std::vector<RegistrarBase<BASE>*>); | ||
} | ||
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<BASE*> plugins() { | ||
std::vector<BASE*> ret; | ||
typedef std::vector<Plugin*> 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<std::vector<RegistrarBase<BASE>*>> registrars_; | ||
}; | ||
|
||
/* | ||
* Static member of the registrar base class. | ||
* BASE is the plug-in base class. | ||
*/ | ||
template<typename BASE> | ||
std::unique_ptr<std::vector<RegistrarBase<BASE>*>> RegistrarBase<BASE>::registrars_; | ||
|
||
/* | ||
* Derived registrar class. | ||
* PLUGIN is the plug-in class (derived from the plug-in base class). | ||
*/ | ||
template<typename PLUGIN> | ||
class Registrar : public RegistrarBase<typename PLUGIN::Base> { | ||
PLUGIN plugin_; | ||
void registerPlugin(RegistrarBase<Plugin>* reg) { | ||
registrars().push_back(static_cast<Plugin*>(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<MyBase>()) { | ||
* p->DoSomething(); | ||
* } | ||
*/ | ||
template<typename T> | ||
std::vector<T*> plugins() { | ||
return RegistrarBase<T>::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> 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<x> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,88 @@ | ||
/** | ||
* @brief Link-time plug-ins demo program | ||
* @version 1.0.0 | ||
* @author Wolfram Rösler | ||
* @date 2018-06-25 | ||
* @copyright MIT license | ||
*/ | ||
|
||
#include <iostream> | ||
#include <string> | ||
#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<PluginBase>()) { | ||
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 ([email protected]) | ||
* @copyright MIT license | ||
*/ | ||
|
||
#include <iostream> | ||
#include <string> | ||
#include <algorithm> | ||
#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<PluginBase> 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; | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find that empty pair of parentheses un-intuitive. I understand why it's needed, but one would expect the iterator to work as usual, i. e.
x->dosomething();
, as in the original example.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I don't like that form at all either. I noticed that down-casting is needed to get the right type object back.