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
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
创建一个临时状态值。这个值会被传递给所有事件处理器,并用于存储临时状态以及累积最终返回值。
较低级别的事件处理器,如 EventHandler
和 EventSet
,各自包含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);
局限性
这种方法的根本局限性在于事件处理程序不能在运行时动态添加或删除,处理程序的集合只能在编译时定义。
由于所有事件处理程序都是通过不可变指针传递的,因此必须使用锁定或细胞来在处理程序中存储状态。