Можете ли вы попросить пользователя указать набор разрешенных типов Base
?В этом случае задача становится простой ( онлайн-демонстрация ):
static_assert(__cplusplus >= 201703L, "example for C++17, but C++14 possible");
#include <iostream>
#include <type_traits>
#include <vector>
struct Callback {
virtual void callback() = 0;
};
template<class... RegisteredBases>
struct CallbackSystem {
template<class Base>
static auto& callbacks_for() {
static std::vector<Callback*> callbacks_for_base_{};
//
// For each `Base`, the callbacks are stored in a different vector.
// This way, we can avoid the in-loop branch (see `actor_impl`).
//
// TODO: consider performance cost of bad memory locality (can be
// improved if necessary).
//
return callbacks_for_base_;
}
template<class Base>
static void addCallback(Callback* callback) {
static_assert((... || std::is_same<Base, RegisteredBases>{}));
callbacks_for<Base>().push_back(callback);
}
template<class Derived, class RegisteredBase>
static void actor_impl() {// called from `actor` for each RegisteredBase
if(std::is_base_of<RegisteredBase, Derived>{}) {// branch outside loop
// if `RegisteredBase` matches then process all its callbacks
for(Callback* callback : callbacks_for<RegisteredBase>()) {
callback->callback();
}
}
}
template<class Derived>
static void actor() {
(actor_impl<Derived, RegisteredBases>(), ...);
}
};
Разрешенные типы Base
регистрируются следующим образом:
using MyCallbacks = CallbackSystem<A, B> {};
Использование читает:
MyCallbacks::addCallback<A>(&call1);
MyCallbacks::addCallback<B>(&call2);
// MyCallbacks::addCallback<B1>(&call2);// compile error (good)
//vvv below is usually in another .cpp
std::cout << R"(should print "A" and "B":)" << std::endl;
MyCallbacks::actor<B1>();
std::cout << R"(should print "A" only:)" << std::endl;
MyCallbacks::actor<C>();
В качестве альтернативы API может быть разработан наоборот: вместо ограничения классов Base
пользователь может указать все классы, которые разрешены в качестве аргумента шаблона actor
.