首先包括:
#include <iostream>
#include <vector>
#include <type_traits>
#include <utility>
#include <functional>
我們使用void_t
detection helper:
template<class ...>
using void_t = void;
我們定義一個性狀使用void_t
檢測方法:
template<class C, class E, class X = void_t<>>
struct has_event_handler :
std::false_type {};
template<class C, class E>
struct has_event_handler<C, E, void_t< decltype(
std::declval<C>().receive(std::declval<const E>())
) >> : std::true_type {};
template<class C, class E>
constexpr bool has_event_handler_v = has_event_handler<C, E>::value;
使用這個,我們可以定義發射器類。可變參數是事件的可管理型:
template<class...> class Emitter;
// Recursive case:
template<class E, class... F>
class Emitter<E, F...> : Emitter<F...> {
public:
// Register:
template<class C>
std::enable_if_t<!has_event_handler_v<C,E>> reg(C& callback) {
Emitter<F...>::reg(callback);
};
template<class C>
std::enable_if_t<has_event_handler_v<C,E>> reg(C& callback) {
handlers_.push_back([&callback](E const& event) { return callback.receive(event); });
Emitter<F...>::reg(callback);
};
void trigger(E const& event)
{
for (auto const& handler : handlers_)
handler(event);
}
template<class G>
void trigger(G const& event)
{
Emitter<F...>::trigger(event);
}
private:
std::vector<std::function<void(const E&)>> handlers_;
};
// Base case:
template<>
class Emitter<> {
public:
template<class C>
void reg(C& callback) {};
template<class E>
void trigger(E const& event)
{
static_assert(!std::is_same<E,E>::value,
"Does not handle this type of event.");
}
};
對於trigger()
部分,另一種解決方案是使用std::enable_if_t<std::is_base_of_v<E, G>>
。
而且我們可以使用它:
// Events
struct E1 {};
struct E2 {};
struct E3 {};
// Handler
struct handler {
void receive(const E1&)
{
std::cerr << "E1\n";
}
void receive(const E2&)
{
std::cerr << "E2\n";
}
};
// Check the trait:
static_assert(has_event_handler_v<handler, E1>, "E1");
static_assert(has_event_handler_v<handler, E2>, "E2");
static_assert(!has_event_handler_v<handler, E3>, "E3");
int main()
{
Emitter<E1, E2> emitter;
handler h;
emitter.reg(h);
emitter.trigger(E1());
emitter.trigger(E2());
}
注:我使用C++ 17的_v
和_t
變種,以便有更短的代碼,但爲了兼容C++ 11你可能想要使用struct
版本(typename std::enable_if<foo>::type
,std::is_base_of<B,D>::value
等)。
更新:它可能會更好用的組合物,而不是繼承爲Emitter
遞歸情況:
template<class E, class... F>
class Emitter<E, F...> {
public:
// Register:
template<class C>
std::enable_if_t<!has_event_handler_v<C,E>> reg(C& callback) {
Emitter<F...>::reg(callback);
};
template<class C>
std::enable_if_t<has_event_handler<C,E>::value> reg(C& callback) {
handlers_.push_back([&callback](E const& event) { return callback.receive(event); });
emitter_.reg(callback);
};
void trigger(E const& event)
{
for (auto const& handler : handlers_)
handler(event);
}
template<class G>
void trigger(G const& event)
{
emitter_.trigger(event);
}
private:
std::vector<std::function<void(const E&)>> handlers_;
Emitter<F...> emitter_;
};
呃.. 。你想要'emitter.reg(s)'實際上做什麼?無論如何,'emitter'是什麼?它是類模板的一個實例嗎?你知道所有的'E1','E2',...類型嗎? – Barry
或多或少。如果我想有一個像Emitter這樣的定義,我必須知道它們。類模板?也許,這取決於實際的實施,這就是我提出的答案。 –
skypjack
我寫了一個小型庫,解決了一個非常類似的問題。 tldr將對訂戶類型使用類型擦除,並通過std :: type_index爲類型擦除的訂戶建立索引。該lib是https://github.com/mmcshane/eventbus。我發佈的評論,而不是一個答案,以避免出現自我推銷。 – mpm