पहले में शामिल हैं:
#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;
इसका उपयोग करके, हम एमिटर क्लास को परिभाषित कर सकते हैं।variadic तर्क घटनाओं के प्रकार यह प्रबंधन कर सकते हैं कर रहे हैं:
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());
}
नोट: मैं के साथ सी ++ 11 क्रम एक छोटा कोड पाने के लिए, लेकिन के लिए संगतता सी ++ 17 से _v
और _t
वेरिएंट इस्तेमाल किया आप शायद 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 (ओं) चाहते हैं? और वैसे भी 'emitter' क्या है? क्या यह एक वर्ग टेम्पलेट का एक उदाहरण है? क्या आप सभी 'ई 1',' ई 2', ... प्रकार पहले से जानते हैं? – Barry
अधिक या कम। अगर मुझे 'एमिटर <ई 1, ई 2, एंडसोऑन>' जैसी परिभाषा चाहिए तो मुझे उन्हें अवश्य जानना चाहिए। कक्षा टेम्पलेट? शायद, यह वास्तविक कार्यान्वयन पर निर्भर करता है, यही वह है जिसे मैंने एक उत्तर के रूप में प्रस्तावित किया। – skypjack
मैंने एक छोटी पुस्तकालय लिखी जो एक बहुत ही समान समस्या को संबोधित करती है। Tldr ग्राहक प्रकार पर टाइप-एरर का उपयोग करना है और टाइप-मिटाए गए ग्राहकों को std :: type_index द्वारा इंडेक्स करना है। Lib है https://github.com/mmcshane/eventbus। मैं आत्म-प्रचार की उपस्थिति से बचने के उत्तर के बजाय एक टिप्पणी के रूप में पोस्ट कर रहा हूं। – mpm