zoukankan      html  css  js  c++  java
  • C++中事件机制的简洁实现

    事件模型是广泛使用的好东西,但是C++标准库里没有现成的,其他实现又复杂或者不优雅,比如需要使用宏。现在VC11可以用在XP下了,那么就痛快的拿起C++11提供的先进设施组合出一个轻便的实现吧。

      为了达到简洁的目的,需要放弃一些特性:

      1、不支持判断函数是否已经绑定过(因为std::function不提供比较方法,自己实现function的话代码又变多了)

      2、需要使用者接收返回的回调函数标识来移除事件绑定(原因同上)

      3、事件没有返回值,不支持回调函数优先级、条件回调等事件高级特性(比如返回所有处理结果中的最大最小值;只回调与指定参数匹配的事件处理函数)

      4、事件参数理论上无限,实际上有限,一般支持0~10个参数(VC11还没有支持变长模板参数,GCC有了。不过可以通过缺省模板参数和偏特化来模拟,所以理论上无限制)

      5、不是线程安全的

      注:3、5两条可以通过引入策略模式来提供灵活支持,就像标准库和Loki做的那样,实现一个完整的事件机制。

    最简单的实现

    复制代码
     1 #include <map>
     2 #include <functional>
     3 
     4 using namespace std;
     5 
     6 
     7 template<class Param1, class Param2>
     8 class Event
     9 {
    10     typedef void HandlerT(Param1, Param2);
    11     int m_handlerId;
    12 
    13 public:
    14     Event() : m_handlerId(0) {}
    15 
    16     template<class FuncT> int addHandler(FuncT func)
    17     {
    18         m_handlers.emplace(m_handlerId, forward<FuncT>(func));
    19         return m_handlerId++;
    20     }
    21 
    22     void removeHandler(int handlerId)
    23     {
    24         m_handlers.erase(handlerId);
    25     }
    26 
    27     void operator ()(Param1 arg1, Param2 arg2)
    28     {
    29         for ( const auto& i : m_handlers )
    30             i.second(arg1, arg2);
    31     }
    32 
    33 private:
    34     map<int, function<HandlerT>> m_handlers;
    35 };
    复制代码

    addHandler把回调函数完美转发给std::function,让标准库来搞定各种重载,然后返回一个标识符用来注销绑定。试一下,工作的不错:

    复制代码
     1 void f1(int, int)
     2 {
     3     puts("f1()");
     4 }
     5 
     6 struct F2
     7 {
     8     void f(int, int)
     9     {
    10         puts("f2()");
    11     }
    12 
    13     void operator ()(int, int)
    14     {
    15         puts("f3()");
    16     }
    17 };
    18 
    19 int _tmain(int argc, _TCHAR* argv[])
    20 {
    21     Event<int, int> e;
    22 
    23     int id = e.addHandler(f1);
    24 
    25     e.removeHandler(id);
    26 
    27     using namespace std::placeholders;
    28 
    29     F2 f2;
    30 
    31     e.addHandler(bind(&F2::f, f2, _1, _2));
    32     e.addHandler(bind(f2, _1, _2));
    33 
    34     e.addHandler([](int, int) {
    35         puts("f4()");
    36     });
    37 
    38     e(1, 2);
    39 
    40     return 0;
    41 } 
    复制代码

    虽然这里有一个小小的缺点,对于仿函数,如果想使用它的指针或引用是不可以直接绑定的,需要这样做: 

    1 e.addHandler(ref(f2));
    2 e.addHandler(ref(*pf2));    // pf2是指向f2的指针

      但是使用仿函数对象指针的情形不多,也不差多敲几个字符,何况在有Lambda表达式的情况下呢?

    改进

    1、有人不喜欢bind,用起来麻烦,放到addhandler里面去:

    复制代码
    1         template<class ObjT, class FuncT> int addHandler(ObjT obj, FuncT func)
    2         {
    3             using namespace std::placeholders;
    4             m_handlers.emplace(m_handlerId, std::bind(func, std::forward<ObjT>(obj), _1, _2));
    5             return m_handlerId++;
    6         }
    复制代码

     2、扩展参数个数。没有变长模板参数,变通一下:

    复制代码
     1 struct NullType {};
     2 
     3 template<class P1 = Private::NullType, class P2 = Private::NullType>
     4 class Event 
     5 {
     6 public:
     7     template<class ObjT, class FuncT> int addHandler(ObjT obj, FuncT func)
     8     {
     9         using namespace std::placeholders;
    10         m_handlers.emplace(m_handlerId, std::bind(func, std::forward<ObjT>(obj), _1, _2));
    11         return m_handlerId++;
    12     }
    13 
    14     void operator ()(P1 arg1, P2 arg2)
    15     {
    16         for ( const auto& i : m_handlers )
    17             i.second(arg1, arg2);
    18     }
    19 };
    20 
    21 template<>
    22 class Event<Private::NullType, Private::NullType>
    23 {
    24 public:
    25     template<class ObjT, class FuncT> int addHandler(ObjT obj, FuncT func)
    26     {
    27         using namespace std::placeholders;
    28         m_handlers.emplace(m_handlerId, std::bind(func, std::forward<ObjT>(obj)));
    29         return m_handlerId++;
    30     }
    31 
    32     void operator ()()
    33     {
    34         for ( const auto& i : m_handlers )
    35             i.second();
    36     }
    37 };
    38 
    39 template<class P1>
    40 class Event<P1, Private::NullType>
    41 {
    42 public:
    43     template<class ObjT, class FuncT> int addHandler(ObjT obj, FuncT func)
    44     {
    45         using namespace std::placeholders;
    46         m_handlers.emplace(m_handlerId, std::bind(func, std::forward<ObjT>(obj), _1));
    47         return m_handlerId++;
    48     }
    49 
    50     void operator ()(P1 arg1)
    51     {
    52         for ( const auto& i : m_handlers )
    53             i.second(arg1);
    54     }
    55 };
    复制代码

    现在支持0~2个参数了。注意到各个模板里有公共代码,提取出来放进基类,然后要做的就是打开文本生成器了

    补充一下:VC里std::function默认最多5个参数,最多支持10个,要在编译开关里设置一下宏_VARIADIC_MAX=10

    完整代码

    代码下载

    View Code

     测试代码

    各种绑定方式

    View Code
     
     
     
    标签: c++ 事件
  • 相关阅读:
    win8.1下安装双系统ubuntu14.04.3
    如何使用cmd
    My Test about Mat
    访问Mat矩阵中的元素并为其赋值
    Mat代码操作
    waitKey()
    ASCII码对照表
    vector 中的clear()
    vector 介绍
    Mat的详解
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2889474.html
Copyright © 2011-2022 走看看