gpt4 book ai didi

c++ - 事件发射器和成员方法自动注册为监听器

转载 作者:IT老高 更新时间:2023-10-28 22:39:08 26 4
gpt4 key购买 nike

这是一个有答案的问题,其目的是邀请读者提出自己的解决方案。
我很确定有比我更聪明的方法,所以我想知道这些解决方案是什么。
请通过添加您自己的答案来分享您的知识!!


目标是创建一个发射器类,它可以用来调度一些事件。

我希望在发声体中具有的一个重要功能是易于使用的注册工具,以将监听器附加到发声体。

换句话说,我不想编写旨在将所有监听器附加到发射器的函数/方法,因为它可能容易出错,而且我发现自己不止一次在寻找由于错过而导致的错误因为那一行代码(当然是注册了 N-th 监听器的一行)。

想象以下结构:

struct E1 { };

struct S {
void receive(const E1 &ev) { /* do something */ }
};

我正在寻找的注册工具就是这样一个解决方案,以下代码行就足够了:

S s;
emitter.reg(s);

仅此而已,即使将来需要在 S 结构中添加更多监听器,例如:

struct E1 { };
struct E2 { };

struct S {
void receive(const E1 &ev) { /* do something */ }
void receive(const E2 &ev) { /* do something */ }
};

我怎么能写出这样的发射器?

最佳答案

首先包括:

#include <iostream>
#include <vector>
#include <type_traits>
#include <utility>
#include <functional>

我们使用 void_t detection helper :

template<class ...>
using void_t = void;

我们定义了一个特征来检测 receive()使用 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());
}

注意:我使用的是 _v_t C++17 的变体为了有一个更短的代码但为了与 C++11 兼容,你可能想要使用 struct版本(typename std::enable_if<foo>::typestd::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_;
};

关于c++ - 事件发射器和成员方法自动注册为监听器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36666934/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com