gpt4 book ai didi

c++ - 使用模板的事件处理

转载 作者:行者123 更新时间:2023-11-30 01:23:27 28 4
gpt4 key购买 nike

我最近一直在研究新的 C++11 标准,并决定创建一个基本的事件处理系统。下面的代码提供了我当前实现的一个小示例。

#include <functional>
#include <vector>
#include <iostream>

template <typename Event>
class EventBroadcaster
{
public:
typedef std::function<void(const Event&)> Connection;

void connect(Connection&& connection)
{
connections.push_back(std::move(connection));
}

void signal(const Event& event)
{
for (const auto& connection : connections)
{
connection(event);
}
}
private:
std::vector<Connection> connections;
};

struct MouseMotion
{
int x = 0;
int y = 0;
};

class Input : public EventBroadcaster<MouseMotion>
{
public:
void process()
{
MouseMotion mouseMotion;
mouseMotion.x = 10;
mouseMotion.y = 20;
signal(mouseMotion);
}
};

int main()
{
int x = 0;
int y = 0;

Input input;

input.connect([&](const MouseMotion& e){
x += e.x;
y += e.y;
});

input.process();

std::cout << x << "," << y << std::endl; // Output: 10,20

return 0;
}

如果 Input 上述解决方案确实工作得很好类只会广播一个事件。然而,可能会出现 Input类(class)希望能够发送KeyPress除了MouseMotion 之外的事件事件。

我考虑过使用多重继承。制作Input继承两者 EventBroadcaster<MouseMotion>EventBroadcaster<KeyPress> .这会导致编译器错误警告不明确的函数。以下答案中提供的解决方案Multiple Inheritance Template Class确实适用于 protected signal功能,但不面向公众connectInput 之外调用的函数类。

除了多重继承之外,我想知道可变参数模板是否可以帮助我解决我的问题。我看过(部分)模板特化和拆包可变参数模板。但是无法提供(优雅的)解决方案。

支持多种事件类型的最佳方式是什么?

最佳答案

我会做 EventBroadcaster<T>一个实现细节,而是公开 EventBroadcasters<Ts...> .

EventBroadcasters<Ts...>拥有EventBroadcaster<Ts>...有模板方法 connect<U>signal<U> , 并将这些转发给 EventBroadcaster<U> .如果 U,这应该无法编译不在 Ts... 中.

所以现在你signal(mouseMotion) ,解析为 signal<MouseMotion>(mouseMotion) , 它连接到正确的广播公司。

当你connect听,同样只要你使用正确的 std::function一切正常。如果没有,你可以传入 Event 的类型。你正在听。

有一些方法可以让它变得更加神奇,并启用您想要的完全正确的重载解析,但这真的很棘手。即,你仪器起来connect执行 SFINAE 并手动将传入的 lambda(通过检查其 operator() )分派(dispatch)到正确的 std::function覆盖。然而,仅仅能够说 connect<MouseMotion>([&](MouseMotion m){...})应该是一个改进。

(SFINAE 技术包括检查列表中的哪些 std::function 类型可以根据您传入的 U 构造,如果有一个唯一的,则使用那个。此外,检查 U 是否是这样的 std::function(或此类的 cv 变体)。它并不难到离谱,但看起来很乱,根据我的经验,大多数人不喜欢这种类型,而更喜欢只指定 <MouseMotion>。 .)

更新:

以下代码块提供了此答案的实现。

#include <functional>
#include <vector>
#include <iostream>

namespace detail
{
template <typename Event>
class EventBroadcaster
{
public:
typedef std::function<void(const Event&)> Connection;

void connect(Connection&& connection)
{
connections.push_back(std::move(connection));
}

void signal(const Event& event)
{
for (const auto& connection : connections)
{
connection(event);
}
}
private:
std::vector<Connection> connections;
};

template <typename T> struct traits
: public traits<decltype(&T::operator())> {};

template <typename C, typename R, typename A>
struct traits<R(C::*)(const A&) const>
{
typedef A type;
};
}

template <typename... Events>
class EventBroadcaster
: detail::EventBroadcaster<Events>...
{
public:
template <typename Connection>
void connect(Connection&& connection)
{
typedef typename detail::traits<Connection>::type Event;
detail::EventBroadcaster<Event>& impl = *this;
impl.connect(std::move(connection));
}

template <typename Event>
void signal(const Event& event)
{
detail::EventBroadcaster<Event>& impl = *this;
impl.signal(event);
}

virtual void processEvents() = 0;
};

struct MouseMotion
{
int x = 0;
int y = 0;
};

struct KeyPress
{
char c;
};

class Input
: public EventBroadcaster<MouseMotion, KeyPress>
{
public:
void processEvents()
{
MouseMotion mouseMotion;
mouseMotion.x = 10;
mouseMotion.y = 20;
signal(mouseMotion);

KeyPress keyPress;
keyPress.c = 'a';
signal(keyPress);
}
};

int main()
{
int x = 0;
int y = 0;
char c = '~';

Input input;

input.connect([&](const MouseMotion& e){
x += e.x;
y += e.y;
});

input.connect([&](const KeyPress& e){
c = e.c;
});

input.processEvents();

std::cout << c << " " << x << "," << y << std::endl; // Output: a 10,20

return 0;
}

关于c++ - 使用模板的事件处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15028609/

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