事件模型是被广泛使用的好东西,但是C++标准库里没有现成的,其他实现又复杂或者不优雅,比如需要使用宏。现在VC11可以用在XP下了,那么就痛快的拿起C++11提供的先进设施组合出一个轻便的实现吧。
为了达到简洁的目的,需要放弃一些特性:
1、不支持判断函数是否已经绑定过(因为std::function不提供比较方法,自己实现function的话代码又变多了)
2、需要使用者接收返回的回调函数标识来移除事件绑定(原因同上)
3、事件没有返回值,不支持回调函数优先级、条件回调等事件高级特性(比如返回所有处理结果中的最大最小值;只回调与指定参数匹配的事件处理函数)
4、事件参数理论上无限,实际上有限,一般支持0~10个参数(VC11还没有支持变长模板参数,GCC有了。不过可以通过缺省模板参数和偏特化来模拟,所以理论上无限制)
5、不是线程安全的
注:3、5两条可以通过引入策略模式来提供灵活支持,就像标准库和Loki做的那样,实现一个完整的事件机制。
最简单的实现
#include <map> #include <functional> using namespace std; template<class Param1, class Param2> class Event { typedef void HandlerT(Param1, Param2); int m_handlerId; public: Event() : m_handlerId(0) {} template<class FuncT> int addHandler(FuncT func) { m_handlers.emplace(m_handlerId, forward<FuncT>(func)); return m_handlerId++; } void removeHandler(int handlerId) { m_handlers.erase(handlerId); } void operator ()(Param1 arg1, Param2 arg2) { for ( const auto& i : m_handlers ) i.second(arg1, arg2); } private: map<int, function<HandlerT>> m_handlers; }; |
addHandler把回调函数完美转发给std::function,让标准库来搞定各种重载,然后返回一个标识符用来注销绑定。试一下,工作的不错:
void f1(int, int) { puts("f1()"); } struct F2 { void f(int, int) { puts("f2()"); } void operator ()(int, int) { puts("f3()"); } }; int _tmain(int argc, _TCHAR* argv[]) { Event<int, int> e; int id = e.addHandler(f1); e.removeHandler(id); using namespace std::placeholders; F2 f2; e.addHandler(bind(&F2::f, f2, _1, _2)); e.addHandler(bind(f2, _1, _2)); e.addHandler([](int, int) { puts("f4()"); }); e(1, 2); return 0; } |