Fork me on GitHub
在C++中实现事件(委托)
C++中实现回调机制的几种方式一文中,我们提到了实现回调的三种方式(C风格的回调函数, Sink方式和Delegate方式)。在面向对象开发中,delegate的方式是最灵活和方便的,因此很早就有人用复杂的模板去模拟, 实现起来很复杂。但是现在借助C++11的functionbind, 我们可以很方便的去实现。下面是我自己的一种实现方式:
  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*= 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++
 
posted on 2013-01-31 20:06  HackerVirus  阅读(333)  评论(0编辑  收藏  举报