Skip to content
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
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,13 @@ namespace {
for (const auto plugin : linktimeplugin::plugins<PluginBase>()) {
plugin->dosomething();
}

// OR
std::for_each(
linktimeplugin::RegistrarBase<PluginBase>::begin(),
linktimeplugin::RegistrarBase<PluginBase>::end(),
[](const auto x) {
(*x)().dosomething();

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.

Copy link
Author

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.

});
```

Complete example: `main.cpp`. To build and run the example program:
Expand Down
130 changes: 39 additions & 91 deletions linktimeplugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
170 changes: 88 additions & 82 deletions main.cpp
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;
}