在程序中使用批量事件通知是很常用的场景,该事件分发器可以绑定多个静态与非静态函数,内部使用标准库的list和function实现,可变模板参数可以拓展到任意长度。
模仿C#中的委托,分为Events、Delegate、Action和Function。以下是继承的结构。
- Events (添加移除静态与实例事件)
- Delegate (执行事件、按实例移除或全部移除)
- Action (Delegate特化版本)
- Function (继承Delegate,实现带返回值列表的执行事件)
- Function<bool> (Function偏特化版本,实现返回值验证是否存在false)
- Delegate (执行事件、按实例移除或全部移除)
- ActionEvents (Event特化版本)
- FunctionEvents (Event特化版本)
Events作为模板需要传入返回值与形参类型,同时Events作为基类,只能使用添加事件和移除事件,并不可以执行。而执行函数是在Delegate层以上的,因为ActionEvents和FunctionEvents只是Events的具体化,Action只是Delegate的具体化,所以就可以有以下的继承关系。
- ActionEvents (Events)
- Action (Delegate)
- FunctionEvents (Events)
- Delegate
- Function
- Function<bool>
- Delegate
Events类应该实现以下几个功能:
- 添加与移除函数指针的事件(普通函数、成员静态函数与无捕获的lambda表达式)
- 添加与移除实例事件(成员实例函数)
- 通过索引来移除闭包lambda
- 提供+=与-=运算符重载(仅函数指针类型)
Delegate类功能实现:
- Invoke执行所有事件
- 按实例对象移除事件
- 移除所有事件
普通的函数指针因为在内存中仅存在一份,所以在移除时可以方便的直接对保存的函数指针进行对比。
带捕获的lambda表达式可以直接转换成std::function类型,采用索引的方式来控制:将函数保存后,返回一个自增的索引,外部可以通过这个索引来移除。或者在创建时传入一个实例对象,最后按实例移除事件。
普通的实例函数就需要使用std::bind对实例进行绑定,但是绑定后就没办法对实例和成员指针进行判断,所以使用成员指针来比较,C++的成员指针必须要求指定的类型,所以使用类模板,并通过继承来做类型擦除,类型擦除后就可以将它们保存到一起,同时取值函数也是一个模板函数,在取值时对实例进行基类至子类的转换。
所有事件的绑定绑定都会返回一个索引值,用来标示该事件,主要用于没有名字函数的移除,在单独移除事件时需要该索引值,在不需要移除单事件或移除全部的情况下该索引可以丢弃。
使用:
{{EJS0}}
输出结果:
{{EJS1}}
该源码你可以在 https://github.com/JomiXedYu/JxCode.CoreLib/blob/main/CoreLib/Events.hpp 找到。
源码:
{{EJS2}}