#事件处理 #事件 #静态 #处理器 #系统 #泛型 #零成本

nightly static-events

创建零成本事件系统的库

4个版本

使用旧Rust 2015

0.1.3 2018年6月16日
0.1.2 2018年6月16日
0.1.1 2018年6月13日
0.1.0 2018年6月13日

#20 in #zero-cost

MIT/Apache

30KB
365

一个通用的零成本事件处理器系统。事件分发应该编译成一个执行所有处理器的普通函数,而不进行动态分发。

由于依赖于特化,该库仅在nightly版本下可用。

请参阅文档以获取更多信息。

该库在MIT和Apache 2.0许可证下双授权。


lib.rs:

一个通用的零成本事件处理器系统。事件分发应该编译成一个执行所有处理器的普通函数,而不进行动态分发。

由于这个crate依赖于特化,你必须在每个使用此库的crate中启用#![feature(specialization)]

#![feature(specialization)]
#[macro_use] extern crate static_events;

基本模型

事件可以是任何实现了Event的类型,并且该类型本身用作事件处理器的键来区分不同的事件。

事件处理器主要由实现了EventRoot的类型定义,它为所有Event提供了一个默认的空操作EventHandler实现,可以被显式的EventHandler实现进一步覆盖。

EventSet是在EventRoot之上的进一步抽象,它具有针对所有Event的单独泛型方法,而不是像EventRoot那样为所有Event提供一个EventHandler的实现。

最后,EventDispatch是最高级别的事件处理特质,它有一个函数,该函数接受一个Event,并返回其RetVal

事件分发

事件分发本质上类似于从单个事件处理器构建的函数调用。它们接收一个事件,并返回一个值(这可以简单地是一个 ())。

事件分发的开始,会调用一次 Event::starting_state 创建一个临时状态值。这个值会被传递给所有事件处理器,并用于存储临时状态以及累积最终返回值。

较低级别的事件处理器,如 EventHandlerEventSet,各自包含5个方法,执行顺序如下
init -> check -> before_event -> on_event -> after_event

它们接收事件本身和当前状态的可变借用,并返回一个状态值,用于控制事件分发的其余部分

  • EvOk 正常继续事件分发。
  • EvCancelStage 阻止执行当前执行的方法和任何后续的事件处理器。
  • EvCancel 立即停止事件分发,进入返回值的计算。

最后,在事件分发的末尾,会在状态上调用 Event::to_return_value 计算最终返回值。在许多情况下,这只是一个空操作。

定义事件

任何模块都可以定义事件。事件是实现了 Event 特性的普通类型,可以使用各种宏进行声明;

  • simple_event! 用于那些直接返回其状态给调用者或根本不使用状态的事件。
  • failable_event! 用于可能失败并返回 Result 的事件。
  • ipc_event! 用于只应由一个监听器处理的事件。
pub struct MyEvent(u32);
simple_event!(MyEvent, u32, 0);

虽然 Event 是稳定的API,并且可以手动实现,但这应该只在特殊情况下进行。

定义事件处理器

使用 EventRoot(一个标记trait)和任意数量的 EventHandler 实现来定义单个事件处理器。

struct MyEventHandler;
impl EventRoot for MyEventHandler { }
impl EventHandler<MyEvent> for MyEventHandler {
    fn on_event(&self, _: &impl EventDispatch, ev: &mut MyEvent, i: &mut u32) -> EventResult {
        *i += ev.0;
        EvOk
    }
}

assert_eq!(MyEventHandler.dispatch(MyEvent(42)), 42);

simple_event_handler! 也可以用于没有状态或参数的处理程序。还存在一个 event_handler! 宏,它可以将 EventHandler 实现添加到现有的结构体中,而不是创建一个新的结构体

simple_event_handler!(MyEventHandler, MyEvent: {
    on_event: |_, ev, i| { *i += ev.0 }
});

最后,可以使用 merged_eventset! 宏来合并多个事件处理程序

simple_event_handler!(MyEventHandler, MyEvent: {
    on_event: |_, ev, i| { *i += ev.0 }
});
simple_event_handler!(MyOtherEventHandler, MyEvent: {
    on_event: |_, ev, i| { *i *= ev.0 }
});

merged_eventset! {
    #[derive(Default)]
    struct SquaringEventHandler<T: EventSet> {
        evh_a: MyEventHandler, evh_b: T,
    }
}

assert_eq!(SquaringEventHandler::<MyOtherEventHandler>::default().dispatch(MyEvent(9)), 81);

局限性

这种方法的根本局限性在于事件处理程序不能在运行时动态添加或删除,处理程序的集合只能在编译时定义。

由于所有事件处理程序都是通过不可变指针传递的,因此必须使用锁定或细胞来在处理程序中存储状态。

依赖项