zoukankan      html  css  js  c++  java
  • 在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++
     
  • 相关阅读:
    WCF 第十三章 可编程站点 为站点创建操作
    WCF 第十三章 可编程站点 所有都与URI相关
    WCF 第十二章 对等网 使用自定义绑定实现消息定向
    WCF 第十三章 可编程站点 使用WebOperationContext
    Using App.Config for user defined runtime parameters
    WCF 第十三章 可编程站点
    WCF 第十三章 可编程站点 使用AJAX和JSON进行网页编程
    WCF 第十二章 总结
    WCF 第十三章 可编程站点 使用WebGet和WebInvoke
    WCF 第十三章 可编程站点 URI和UriTemplates
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2888138.html
Copyright © 2011-2022 走看看