1
0
mirror of https://github.com/wqking/eventpp.git synced 2024-12-26 15:52:40 +08:00
eventpp/doc/cn/introduction.md
marsCatXDU 55d49440be Add Chinese translation
Signed-off-by: marsCatXDU <marscatcn@live.com>
2022-07-23 20:21:02 +08:00

5.1 KiB
Raw Permalink Blame History

eventpp 库介绍

eventpp 有三个核心的类CallbackList, EventDispatcher 和 EventQueue ,三者各有其特定的用途。

CallbackList 类

CallbackList 是 eventpp 中最基础的类EventDispatcher 和 EventQueue 类的实现都建立在 CallbackList 之上。

CallbackList 会维护一个回调函数列表。当一个 CallbackList 被调用时,该 CallbackList 会逐个调用其中的回调函数。CallbackList 可以类比为 Qt 中的信号槽系统,以及某些 Windows API 中的回调函数指针(比如 ReadFileEx 中的 IpCompletionRoutine

这里的“回调函数”可以是任何能够回调的目标——函数、函数指针、成员函数指针、lambda表达式、函数对象等。

eventpp 中的 CallbackList 相当于是 Qt 这类事件系统中的 “信号”,但 eventpp 中并没有特定的相当于 “槽”(回调函数)的东西 —— 在 eventpp 中,任何可调用的目标都可以是槽(回调函数)

当应用场景中的事件种类很少时,直接用 CallbackList 即可满足需求:为每个事件创建一个 CallbackList每个 CallbackList 都可以有不同的原型( prototype ),例如:

eventpp::CallbackList<void()> onStart;
eventpp::CallbackList<void(MyStopReason)> onStop;

当程序中有成百上千个事件时( GUI 和游戏程序中经常会有这么多的事件),上面这种写法就难以应付了——为每个事件都单独创建 CallbackList 肯定不是什么好选择。针对存在大量事件的场景eventpp 设计了 EventDispatcher

EventDispatcher 类

EventDispatcher 类似于 std::map<EventType, CallbackList> ,能根据 EventType 寻找对应 CallbackList。

EventDispatcher 维护一个 <EventType, CallbackList> 映射表。在进行事件分发时, EventDispatcher 会根据事件类型EventType查找并调用对应的回调列表CallbackList 。该调用过程是同步的,监听器会在 EventDispatcher::dispatch 被调用时触发。

EventDispatcher 适用于事件种类繁多或无法预先确定事件数量的场景。每个事件都可以根据事件类型进行分类,例如:

enum class MyEventType
{
    redraw,
    mouseDown,
    mouseUp,
    // 后面也许还有 2000 个事件
};

struct MyEvent
{
    MyEventType type;
    // 此处定义所有事件可能会需要的数据
};

struct MyEventPolicies
{
    static MyEventType getEvent(const MyEvent &e) {
        return e.type;
    }
};

eventpp::EventDispatcher<MyEventType, void(const MyEvent &), MyEventPolicies> dispatcher;
dispatcher.dispatch(MyEvent { MyEventType::redraw });

(提示:若想了解上面代码中的 MyEventPolicies ,请阅读https://github.com/wqking/eventpp/blob/master/doc/policies.md 文档。本篇文档中可以暂且只关注分发器: eventpp::EventDispatcher<MyEventType, void(const MyEvent &)> dispatcher

EventDispatcher 的缺点是,分发器中所有事件都必须有相同的回调原型(例如示例代码中的 void(const MyEvnet &))。通常可以通过下面的方案来规避该缺点:将 Event 基类作为回调函数的参数,然后所有的事件都通过继承 Event 来传递他们自己的数据。在示例代码中,MyEvent 就是事件的基类,回调函数接收 const MyEvent &

EventQueue 类

EventQueue 包含了 EventDispatcher 的所有特性并在此基础上添加了事件队列。注意EventQueue 并不是 EventDispatcher 的子类,因此不要进行类型转换。

EventQueue 是异步的。事件会在调用 EventQueue::enqueue 时被保存在队列中,并在 EventQueue::process 被调用时被处理。

EventQueue 相当于 Qt 中的事件系统QEvent或 Windows API 中的消息处理( message processing )。

eventpp::EventQueue<int, void (const std::string &, const bool)> queue;

// 将事件加入队列。第一个参数永远是事件类型
// 监听器不会在进入队列期间被触发
queue.enqueue(3, "Hello", true);
queue.enqueue(5, "World", false);

// 处理事件队列,运行队列中的所有事件
queue.process();

线程安全

所有类都是线程安全的,可以在多个线程中同时调用所有的公共函数。如果出现问题,请到 eventpp 的项目仓库报告 bug。 本库能够确保每个单独函数调用的整体性,如 EventDispatcher::appendListener, CallbackList::remove 但无法确保多个线程中的操作执行顺序。例如若在某个线程分发一个事件的同一时刻另一个线程移除了一个监听器listener则被移除的监听器仍有可能在其被移除之后触发。

异常安全

所有的类都不会抛出异常。但库代码所依赖的代码可能会在下列情况发生时抛出异常:

  1. 内存耗尽,无法分配新的内存空间;
  2. 监听器(回调函数)在复制、移动、对比或调用时抛出异常

几乎所有操作都有很强的异常安全性,这意味着下层数据抛出异常时能够保持其原始值。唯一的例外是 EventQueue::process:在抛出异常时,剩余的事件就不再会被分发了,而且队列会被清空。