好得很程序员自学网

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

在C++中实现事件(委托)

在C++中实现事件(委托)

在 C++中实现回调机制的几种方式 一文中,我们提到了实现回调的三种方式(C风格的回调函数, Sink方式和Delegate方式)。在面向对象开发中,delegate的方式是最灵活和方便的,因此很早就有人用复杂的模板去模拟, 实现起来很复杂。但是现在借助C++11的 function 和 bind , 我们可以很方便的去实现。下面是我自己的一种实现方式:

  1  #pragma once
  2  
  3  #include <functional>
  4  #include <algorithm>
  5  #include <vector>
  6  #include <assert.h>
  7  
  8   namespace  Common
  9  {
 10      typedef  void * cookie_type;
 11  
 12      template<typename TR, typename T1, typename T2>
 13       class  CEvent
 14      {
 15       public :
 16          typedef TR return_type;
 17          typedef T1 first_type;
 18          typedef T2 second_type;
 19  
 20          typedef std::function<return_type (first_type, second_type)> handler_type;
 21  
 22          ~CEvent()
 23          {
 24              Clear();
 25          }
 26  
 27          return_type  operator ()(first_type p1, second_type p2)
 28          {
 29              return_type ret = return_type();
 30              size_t size = _handlers.size();
 31               for (size_t i=0; i<size; ++i)
 32              {
 33                  ret = _handlers[i]-> operator ()(p1, p2);
 34              }
 35               return  ret;
 36          }
 37  
 38          cookie_type AddHandler(std::function<return_type (first_type, second_type)> h)
 39          {
 40              CEventHandler*p =  new (nothrow)  CEventHandler(h);
 41               if (p != nullptr) _handlers.push_back(p);
 42               return  (cookie_type)p;
 43          }
 44  
 45          template<typename class_type, typename class_fun>
 46          cookie_type AddHandler(class_type* pThis, class_fun f)
 47          {
 48              CEventHandler* p =  new (nothrow) CEventHandler(pThis, f);
 49               if (p != nullptr) _handlers.push_back(p);
 50               return  (cookie_type)p;
 51          }
 52  
 53           void  RemoveHandler(cookie_type cookie)
 54          {
 55              CEventHandler* p = (CEventHandler*)cookie;
 56  
 57              auto itr = std::find(_handlers.begin(), _handlers.end(), p);
 58               if (itr != _handlers.end())
 59              {
 60                  _handlers.erase(itr);
 61                  delete p;
 62              }
 63               else
 64              {
 65                  assert( false );
 66              }
 67          }
 68  
 69           void  Clear()
 70          {
 71               if (!_handlers.empty())
 72              {
 73                   int  n = _handlers.size();
 74                  std::for_each(_handlers.begin(), _handlers.end(), [](CEventHandler* p)
 75                  { 
 76                      assert(p != nullptr);
 77                      delete p;
 78                  });
 79                  _handlers.clear();        
 80              }
 81          }
 82  
 83       private :
 84           class  CEventHandler 
 85          {
 86           public :
 87              CEventHandler(handler_type h)
 88              {
 89                  _handler = h;
 90                  assert(_handler != nullptr);
 91              }
 92  
 93              template<typename class_type, typename class_fun>
 94              CEventHandler(class_type* pThis, class_fun object_function)
 95              {
 96                   using   namespace  std::placeholders;
 97                  _handler = std::bind(object_function, pThis, _1, _2);
 98                  assert(_handler != nullptr);
 99              }
100  
101              return_type  operator ()(first_type p1, second_type p2)
102              {
103                  return_type ret = return_type();
104                  assert(_handler != nullptr);
105                   if (_handler != nullptr) ret = _handler(p1, p2);
106                   return  ret;
107              }
108  
109              handler_type _handler;
110          };
111  
112  
113       private :
114          std::vector<CEventHandler*> _handlers;
115      };
116  
117  }  // Common


大概实现思想是我们通过一个内置的 CEventHandler 类来封装处理函数,我们可以通过AddHandler来添加事件处理函数,添加时会返回一个Cookie,我们可以通过该Cookie来RemoveHandler, 下面是测试代码:

 1   //  EventTest.cpp : Defines the entry point for the console application.
 2   //
 3  
 4   #include  " stdafx.h "
 5  
 6   #include  < iostream >
 7   #include  " event1.h "
 8  
 9   using   namespace  std;
10  
11   class  CObjectX 
12   {
13  
14   };
15  
16   class  CClickEventArgs:  public  CObjectX
17   {
18  
19   };
20  
21  
22   class  CButton:  public  CObjectX
23   {
24   public :
25       void  FireClick()
26       {
27           CClickEventArgs args;
28           OnClicked( this , args);
29       }
30  
31       Common::CEvent < int , CObjectX * , CClickEventArgs &>  OnClicked;
32   };
33  
34  
35   class  CMyClass 
36   {
37   public :
38       int  OnBtuttonClicked(CObjectX *  pButton, CClickEventArgs &  args)
39       {
40           cout  <<   " CMyClass: Receive button clicked event "   <<  endl;
41           return   1 ;
42       }
43   };
44  
45   int  OnBtuttonClicked_C_fun(CObjectX *  pButton, CClickEventArgs &  args)
46   {
47       cout  <<   " C Style Function: Receive button clicked event "   <<  endl;
48       return   1 ;
49   }
50  
51  
52   class  CMyFunObj
53   {
54   public :
55       int   operator ()(CObjectX *  pButton, CClickEventArgs &  args)
56       {
57           cout  <<   " Functor: Receive button clicked event "   <<  endl;
58           return   1 ;
59       }
60   };
61  
62   int  _tmain( int  argc, _TCHAR *  argv[])
63   {
64       using   namespace  std::placeholders;
65  
66       CButton btn;
67  
68       CMyClass obj;
69       Common::cookie_type c1  =  btn.OnClicked.AddHandler( & obj,  & CMyClass::OnBtuttonClicked);
70  
71       Common::cookie_type c2  =  btn.OnClicked.AddHandler(OnBtuttonClicked_C_fun);
72  
73       CMyFunObj functor;
74       Common::cookie_type c3  =  btn.OnClicked.AddHandler(functor);
75  
76       btn.FireClick();
77  
78  
79       btn.OnClicked.RemoveHandler(c2);
80  
81       std::cout  <<  endl;
82  
83  
84       btn.FireClick();
85  
86       system( " pause " );
87  
88       return   0 ;
89   }
90  


以下是测试结果:


 可以看到, 我们在普通C函数, 类成员函数和仿函数(functor)中都测试通过。

另外对于事件函数返回值为void的情况,会编译出错,我们需要偏特化一下:

  1       template <  typename T1, typename T2 >
  2       class  CEvent < void , T1, T2 >
  3       {
  4       public :
  5           typedef  void  return_type;
  6           typedef T1 first_type;
  7           typedef T2 second_type;
  8  
  9           typedef std::function < return_type (first_type, second_type) >  handler_type;
 10  
 11           ~ CEvent()
 12           {
 13               Clear();
 14           }
 15  
 16           return_type  operator ()(first_type p1, second_type p2)
 17           {
 18               size_t size  =  _handlers.size();
 19               for (size_t i = 0 ; i < size;  ++ i)
 20               {
 21                   _handlers[i] -> operator ()(p1, p2);
 22               }
 23           }
 24  
 25           cookie_type AddHandler(std::function < return_type (first_type, second_type) >  h)
 26           {
 27               CEventHandler * p  =   new (nothrow)  CEventHandler(h);
 28               if (p  !=  nullptr) _handlers.push_back(p);
 29               return  (cookie_type)p;
 30           }
 31  
 32           template < typename class_type, typename class_fun >
 33           cookie_type AddHandler(class_type *  pThis, class_fun f)
 34           {
 35               CEventHandler *  p  =   new (nothrow) CEventHandler(pThis, f);
 36               if (p  !=  nullptr) _handlers.push_back(p);
 37               return  (cookie_type)p;
 38           }
 39  
 40           void  RemoveHandler(cookie_type cookie)
 41           {
 42               CEventHandler *  p  =  (CEventHandler * )cookie;
 43  
 44               auto itr  =  std::find(_handlers.begin(), _handlers.end(), p);
 45               if (itr  !=  _handlers.end())
 46               {
 47                   _handlers.erase(itr);
 48                   delete p;
 49               }
 50               else
 51               {
 52                   assert( false );
 53               }
 54           }
 55  
 56           void  Clear()
 57           {
 58               if ( ! _handlers.empty())
 59               {
 60                   int  n  =  _handlers.size();
 61                   std::for_each(_handlers.begin(), _handlers.end(), [](CEventHandler *  p)
 62                   { 
 63                       assert(p  !=  nullptr);
 64                       delete p;
 65                   });
 66                   _handlers.clear();        
 67               }
 68           }
 69  
 70       private :
 71           class  CEventHandler 
 72           {
 73           public :
 74               CEventHandler(handler_type h)
 75               {
 76                   _handler  =  h;
 77                   assert(_handler  !=  nullptr);
 78               }
 79  
 80               template < typename class_type, typename class_fun >
 81               CEventHandler(class_type *  pThis, class_fun object_function)
 82               {
 83                   using   namespace  std::placeholders;
 84                   _handler  =  std::bind(object_function, pThis, _1, _2);
 85                   assert(_handler  !=  nullptr);
 86               }
 87  
 88               return_type  operator ()(first_type p1, second_type p2)
 89               {
 90                   assert(_handler  !=  nullptr);
 91                   if (_handler  !=  nullptr) _handler(p1, p2);
 92               }
 93  
 94               handler_type _handler;
 95           };
 96  
 97  
 98       private :
 99           std::vector < CEventHandler *>  _handlers;
100       };


最后谈一下在写这个代码中遇到的问题:
(1)不知道你能不能发现下面代码的问题, 我在写代码时就栽在这里了:
   1      vector < int *>   v;

 2      int *  p1  =   new   int ( 1 );
 3      v.push_back(p1);
 4      int *  p2  =   new   int ( 2 );
 5      v.push_back(p2);
 6  
 7      // 尝试删除所有值为p1的项
 8      auto itr  =  remove(v.begin(), v.end(), p1);
 9      for_each(itr, v.end(), []( int *  p){delete p;});
10      v.erase(itr, v.end());


(2)我们想把cookei_type放到类里面去, 类似这样:

1       template < typename TR, typename T1, typename T2 >
2       class  CEvent
3       {
4       public :
5           typedef TR return_type;
6           typedef T1 first_type;
7           typedef T2 second_type;
8           typedef  void *  cookie_type;


可发现要这样使用:

Common::CEvent < int , CObjectX * , CClickEventArgs &> ::cookie_type c1  =  btn.OnClicked.AddHandler( & obj,  & CMyClass::OnBtuttonClicked);

太不方便了, 不知道大家有没有好的方法。

注:上面的代码还没有经过真正商业使用,如果有问题欢迎指出。

 

在上文  在C++中实现事件(委托)  中我们实现的C#里委托方式的事件处理, 虽然使用很方便,但是感觉似乎少了一点C#的味道, 下面我们尝试把它改成真正的C#版。

其实要改成真正的C#版,我们主要要做2件事, 一是吧CEventHandler放到外面,可以让外部直接构造, 二是实现operator +=和operator -=, 下面是我的实现代码:

#pragma once

#include <functional>
#include <algorithm>
#include <vector>
#include <assert.h>

namespace  Common
{
    typedef  void * cookie_type;

    template<typename TR, typename T1, typename T2>
     class  CEventHandler 
    {
     public :
        typedef TR return_type;
        typedef T1 first_type;
        typedef T2 second_type;

        typedef std::function<return_type (first_type, second_type)> handler_type;

        CEventHandler( const  CEventHandler& h)
        {
            _handler = h._handler;
            assert(_handler != nullptr);
        }

        CEventHandler(handler_type h)
        {
            _handler = h;
            assert(_handler != nullptr);
        }

        template<typename class_type, typename class_fun>
        CEventHandler(class_type* pThis, class_fun object_function)
        {
             using   namespace  std::placeholders;
            _handler = std::bind(object_function, pThis, _1, _2);
            assert(_handler != nullptr);
        }

        return_type  operator ()(first_type p1, second_type p2)
        {
            return_type ret = return_type();
            assert(_handler != nullptr);
             if (_handler != nullptr) ret = _handler(p1, p2);
             return  ret;
        }

        handler_type _handler;
    };

    template<typename EventHandler>
     class  CEvent
    {
     public :
        typedef EventHandler event_handler_type;
        typedef typename event_handler_type::return_type return_type;
        typedef typename event_handler_type::first_type first_type;
        typedef typename event_handler_type::second_type second_type;
        typedef typename event_handler_type::handler_type handler_type;

        ~CEvent()
        {
            Clear();
        }

        return_type  operator ()(first_type p1, second_type p2)
        {
            return_type ret = return_type();
            size_t size = _handlers.size();
             for (auto p : _handlers)
            {
                ret = p-> operator ()(p1, p2);
            }
             return  ret;
        }

        cookie_type AddHandler( const  event_handler_type& h)
        {
            event_handler_type*p =  new (nothrow)  event_handler_type(h);
             if (p != nullptr) _handlers.push_back(p);
             return  (cookie_type)p;
        }

         void  RemoveHandler(cookie_type cookie)
        {
            event_handler_type* p = (event_handler_type*)cookie;

            auto itr = std::find(_handlers.begin(), _handlers.end(), p);
             if (itr != _handlers.end())
            {
                _handlers.erase(itr);
                delete p;
            }
             else
            {
                assert( false );
            }
        }

        cookie_type  operator  += ( const  event_handler_type& pHandler)
        {
             return  AddHandler(pHandler);
        }

         void   operator  -= (cookie_type cookie)
        {
            RemoveHandler(cookie);
        }

         void  Clear()
        {
             if (!_handlers.empty())
            {
                 int  n = _handlers.size();
                std::for_each(_handlers.begin(), _handlers.end(), [](event_handler_type* p)
                { 
                    assert(p != nullptr);
                    delete p;
                });
                _handlers.clear();        
            }
        }

     private :
        std::vector<event_handler_type*> _handlers;
    };

}  // Common


然后我们就可以这样使用了:

//  EventTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>
#include "event2.h"

using   namespace  std;

class  CObjectX 
{

};

class  CClickEventArgs:  public  CObjectX
{

};


class  CButton:  public  CObjectX
{
public :
     void  FireClick()
    {
        CClickEventArgs args;
        OnClicked( this , args);
    }

    typedef Common::CEventHandler< int , CObjectX*, CClickEventArgs&> ButtonClickEventHandler;
    Common::CEvent<ButtonClickEventHandler> OnClicked;
};


class  CMyClass 
{
public :
     int  OnBtuttonClicked(CObjectX* pButton, CClickEventArgs& args)
    {
        cout << "CMyClass: Receive button clicked event" << endl;
         return  1;
    }
};

int  OnBtuttonClicked_C_fun(CObjectX* pButton, CClickEventArgs& args)
{
    cout << "C Style Function: Receive button clicked event" << endl;
     return  1;
}


class  CMyFunObj
{
public :
     int   operator ()(CObjectX* pButton, CClickEventArgs& args)
    {
        cout << "Functor: Receive button clicked event" << endl;
         return  1;    
    }
};

int  _tmain( int  argc, _TCHAR* argv[])
{
    CButton btn;

    CMyClass obj;
    Common::cookie_type c1 = btn.OnClicked += CButton::ButtonClickEventHandler(&obj, &CMyClass::OnBtuttonClicked);

    Common::cookie_type c2 = btn.OnClicked += CButton::ButtonClickEventHandler(OnBtuttonClicked_C_fun);

    CMyFunObj functor;
    Common::cookie_type c3 = btn.OnClicked += CButton::ButtonClickEventHandler(functor);

    btn.FireClick();

    btn.OnClicked -= c2;

    std::cout << endl;

    btn.FireClick();

    system("pause");

     return  0;
}


怎么样,是不是感觉和C#一样了 !


最后,我们比较一下2种实现方式:
第一种方法把委托函数类型封装起来了,对外部来说是透明的, 使用起来更简单。
第二种方式把委托函数的类型暴露了出来, 适用于 事件处理函数类型各异, 比较强调事件处理函数类型的场合。

其实对于C++来说,个人觉得还是第一种方式更合理, 不知道大家怎么看?

 

 

分类:  C++

标签:  delegate event callback C++

 

作者: Leo_wl

    

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

    

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

版权信息

查看更多关于在C++中实现事件(委托)的详细内容...

  阅读:43次