好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

C++中事件机制的简洁实现

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个参数了。注意到各个模板里有公共代码,提取出来放进基类,然后要做的就是打开文本生成器了

完整代码

代码下载

View Code

  测试代码

各种绑定方式

View Code

 

 

 

标签:  c++ 事件

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于C++中事件机制的简洁实现的详细内容...

  阅读:42次