Signed-off-by: marsCatXDU <marscatcn@live.com>
8.3 KiB
Mixins
目录
介绍
Mixin 用于向 EventDispatcher / EventQueue 继承层次中注入代码,以扩展它们的功能。本文将以 EventDispatcher 为例进行介绍,EventQueue 的 mixin 相关用法与 EventDispatcher 完全一致。
原有的 EventDispatcher 继承层次如下:
EventDispatcher <- EventDispatcherBase
EventDispatcher
是一个空类,其继承来自 EventDispatcherBase
的所有函数和数据。
在注入两个 mixin 类 A 、 B 后,层次如下:
EventDispatcher <- MixinA <- MixinB <- EventDispatcherBase
mixin 能够使用所有 EventDispatcherBase 中的所有 public 和 protected 成员(类型、函数和数据)。mixin 中的所有公共成员对用户来说都是可见、可用的。
定义一个 mixin
一个 mixin 是有着一个模板参数的模板类。mixin 必须继承其模板参数。一个典型的 mixin 如下:
template <typename Base>
class MyMixin : public Base
{
};
将 mixin 注入到 EventDispatcher
想要启用 mixin ,需要将 mixin 加入到策略类的 Mixins
类型中。例如,要启动 MixinFilter
,可以像下面这样来定义调度器
struct MyPolicies {
using Mixins = eventpp::MixinList<eventpp::MixinFilter>;
};
eventpp::EventDispatcher<int, void (), MyPolicies> dispatcher;
若有多个 mixin ,像是 using Mixins = eventpp::MixinList<MixinA, MixinB, MixinC>
,则继承层次如下:
EventDispatcher <- MixinA <- MixinB <- MixinC <- EventDispatcherBase
最前面的排在继承层次的最底部
可选拦截点( Optional interceptor points )
一个 mixin 可以有一些具有特殊名称的函数,这些函数会被在特定的时候调用。这些特殊的函数必须是 public 的。当前只有一个特殊函数,如下:
template <typename ...Args>
bool mixinBeforeDispatch(Args && ...args) const;
mixinBeforeDispatch
会在 EventDispatcher、EventQueue 开始调度事件之前被调用,其接收所有传给 EventDispatcher::dispatch 的参数,除了所有的参数都是以左值引用的方式传递的,无论它们在回调函数原型中是否被引用(无法修改 const 引用)。所以该函数能够修改实参,让后续的监听器都看到修改后的值。
因此该函数可以修改实参,所有的监听器看到的参数都是修改后的值。
该函数返回 true
时将继续调度,返回 false
时会停止继续调度。
对于多 mixin 的情况,该函数会被按照这些 mixin 出现在策略类的 MixinList 中的顺序来执行。
MixinFilter
MixinFilter 可以在调度开始前过滤或修改所有的事件。
MixinFilter::appendFilter(filter)
向调度器中添加新的事件过滤器。filter
接收参数的类型是带有左值引用类型的回调函数原型。
所有的事件都会在执行任何监听器前触发事件过滤器的执行。
因为所有的实参都是以左值引用的形式传递的,因此事件过滤器也能修改这些实参,无论这些实参是否被在回调原型中引用(当然,const 引用是改不了的)。
下面的表格展示了事件过滤器如何接收实参
回调原型的实参类型 | 过滤器收到的实参类型 | 过滤器是否能修改实参? | 备注 |
---|---|---|---|
int, const int | int &, int & | 是 | 抛弃 const 值的不变性 |
int &, std::string & | int &, std::string & | 是 | |
const int &, const int * | const int &, const int * & | 否 | 必须保持引用/指针的不变性 |
事件过滤器强大且实用,下面是一些用例示例
- 捕捉并阻塞所有感兴趣的事件。例如,在一个 GUI 窗口系统中,所有的窗口都能接收到鼠标的点击事件。然而,当一个窗口正在被鼠标拖拽时,只有被拖拽的窗口才应该能接收到鼠标事件,即使鼠标正在其他窗口上移动也应如此。所以当开始拖动时,窗口可以添加一个过滤器,该过滤器重定向所有发到窗口的鼠标事件,阻止其他窗口的监听器获得鼠标事件,但扔放行鼠标事件外的所有其他事件。
- 设置捕捉所有事件的监听器。例如,在一个电话本系统中,系统根据动作(action)来发送事件(删除添加电话号、查找电话号等)。如果想要在该系统中实现这样的一个模块:其只需要取电话号的区号,而不关心具体发生了的动作,该怎么做呢?一种方法是让该模块可以监听所有的事件(添加、删除、查找),但这种方法比较脆弱——若添加了一种新的动作事件,而忘记了为该模块定义相应的监听逻辑,就会导致出现未定义的行为。更好的方法是为该模块添加一个过滤器,并在过滤器中检查区号。
公共类型
FilterHandle
:appendFilter 返回的句柄类型。过滤器句柄可用于移除过滤器。可以通过将 FilterHandle 实例转换为 bool 类型来检查其是否为空,当值为 false 时,该句柄为空。FilterHandle
是可拷贝的。
函数
FilterHandle appendFilter(const Filter & filter);
将 filter
添加进调度器。
该函数将返回一个可用于 removeFilter
的过滤器句柄
bool removeFilter(const FilterHandle & filterHandle);
从调度其中移除一个过滤器。
当过滤器被成功移除时返回 true。
MixinFilter 示例代码
代码
struct MyPolicies {
using Mixins = eventpp::MixinList<eventpp::MixinFilter>;
};
eventpp::EventDispatcher<int, void (int e, int i, std::string), MyPolicies> dispatcher;
dispatcher.appendListener(3, [](const int e, const int i, const std::string & s) {
std::cout
<< "Got event 3, i was 1 but actual is " << i
<< " s was Hello but actual is " << s
<< std::endl
;
});
dispatcher.appendListener(5, [](const int e, const int i, const std::string & s) {
std::cout << "Shout not got event 5" << std::endl;
});
// 添加三个事件过滤器.
// 第一个过滤器将输入的实参改为其他值,然后后续的过滤器及监听器将看到修改后的值
dispatcher.appendFilter([](const int e, int & i, std::string & s) -> bool {
std::cout << "Filter 1, e is " << e << " passed in i is " << i << " s is " << s << std::endl;
i = 38;
s = "Hi";
std::cout << "Filter 1, changed i is " << i << " s is " << s << std::endl;
return true;
});
// 第二个过滤器将所有 5 事件都过滤出来。因此监听事件 5 的过滤器都不会被触发。
// 第三个过滤器也不会在事件 5 触发
dispatcher.appendFilter([](const int e, int & i, std::string & s) -> bool {
std::cout << "Filter 2, e is " << e << " passed in i is " << i << " s is " << s << std::endl;
if(e == 5) {
return false;
}
return true;
});
// 第三个过滤器只打印输入的实参
dispatcher.appendFilter([](const int e, int & i, std::string & s) -> bool {
std::cout << "Filter 3, e is " << e << " passed in i is " << i << " s is " << s << std::endl;
return true;
});
// 调度所有事件,第一个实参总是事件类型
dispatcher.dispatch(3, 1, "Hello");
dispatcher.dispatch(5, 2, "World");
输出
Filter 1, e is 3 passed in i is 1 s is Hello
Filter 1, changed i is 38 s is Hi
Filter 2, e is 3 passed in i is 38 s is Hi
Filter 3, e is 3 passed in i is 38 s is Hi
Got event 3, i was 1 but actual is 38 s was Hello but actual is Hi
Filter 1, e is 5 passed in i is 2 s is World
Filter 1, changed i is 38 s is Hi
Filter 2, e is 5 passed in i is 38 s is Hi