清风竹林

ぷ雪飘绛梅映残红
   ぷ花舞霜飞映苍松
     ----- Do more,suffer less

VC6中的简易delegate实现

VC6中的简易delegate实现

版本:0.1

最后修改:2009-09-01

撰写:李现民


其实原本不想自己写C++委托类的,因为CodeProject已经有许多相关讨论了,国内的前辈也写了很多,但经过一一试用后我无奈的发现它们无一例外的都使用了大量的template技巧,而一些像偏特化之类的template特性在VC6中并没有得到支持。于是只好自己动手了,虽然粗陋,但好在还勉强能胜任一些工作。非VC6平台的同学请参考别的实现。

代码主要参考了jfwan的《一个C#delegateC++中的实现》一文:

#ifndef _LIB_DELEGATE_HPP_INCLUDED_
#define _LIB_DELEGATE_HPP_INCLUDED_

#ifdef WIN32
#pragma warning(disable:
4786)
#endif

#include 
<list>
#include 
<cassert>

namespace lib
{
    
namespace inner_delegate
    {
        
// base class for delegate item
        template<typename ReturnType, typename Param1>
        
class item_base
        {
        
public:
            
virtual int get_group(voidconst= 0;
            
virtual void invoke(Param1 p1) = 0;        // note: vc6 does not support "return void", so will not return anything
        };

        
// delegate item for invoke common functions
        template <typename ReturnType, typename Param1>
        
class item_common_func: public item_base<ReturnType, Param1>
        {
        
public:
            typedef ReturnType (
*lpfnProcess)(Param1 p1);

        
public:
            item_common_func(lpfnProcess pfn)
            {
                assert(NULL
!= pfn);
                _pfn        
= pfn;
            }    

            
// fetch the group of the delegate item
            virtual int get_group(voidconst { return int(_pfn); }

            
// do operation
            virtual void invoke(Param1 p1)
            {
                assert(NULL
!= _pfn);
                (
*_pfn)(p1);
            }

        
private:
            lpfnProcess        _pfn;
        };

        
// delegate item for invoke member functions 
        template <typename ObjectType, typename ReturnType, typename Param1>
        
class item_member_func: public item_base<ReturnType, Param1>
        {
        
public:
            typedef ReturnType (ObjectType::
*lpfnProcess)(Param1 p1);

        
public:
            item_member_func(ObjectType
* pObject, lpfnProcess pfn)
            {
                assert(NULL
!= pObject && NULL!= pfn);
                _pObject    
= pObject;
                _pfn        
= pfn;
            }    

            
// fetch the group of the delegate item
            virtual int get_group(voidconst { return int(_pObject); }

            
// do operation
            virtual void invoke(Param1 p1)
            {
                assert(NULL
!= _pObject && NULL!= _pfn);
                (_pObject
->*_pfn)(p1);
            }

        
private:
            ObjectType
*        _pObject;
            lpfnProcess        _pfn;
        };
    }

    
// create an delegate item for invoke
    template<typename ObjectType, typename ReturnType, typename Param1>
    inline inner_delegate::item_base
<ReturnType, Param1>* make_delegate(ObjectType* pObject, ReturnType (ObjectType::*pfn)(Param1 param1))
    {
        
if (NULL!= pObject && NULL!= pfn)
        {
            
return new inner_delegate::item_member_func<ObjectType, ReturnType, Param1>(pObject, pfn);
        }

        
return NULL;
    }

    
// delegate
    template<typename ReturnType, typename Param1>
    
class delegate
    {
    
private:
        typedef inner_delegate::item_base
<ReturnType, Param1>            ItemType;
        typedef std::list
<ItemType*>                                    ItemPack;
        typedef ReturnType (
*lpfnCommonFunc)(Param1);

    
public:
        typedef typename ItemPack::iterator                                iterator;
        typedef typename ItemPack::const_iterator                        const_iterator;
        typedef typename ItemPack::reference                            reference;
        typedef typename ItemPack::const_reference                        const_reference;
        typedef typename ItemPack::difference_type                        difference_type;
        typedef typename ItemPack::value_type                            value_type;

    
public:
        
delegate(void) { }
        
~delegate(void) { clear(); }
        
operator bool() const { return !_uItems.empty(); }                // Is valid for invoke function

        
// operator+=    note: this function does not return "*this", but return "iterator"
        iterator operator+= (lpfnCommonFunc pfn) { return (NULL!= pfn)? (operator+= (new inner_delegate::item_common_func<ReturnType, Param1>(pfn))): _uItems.end(); }

        
// operator+=    note: this function does not return "*this", but return "iterator"
        iterator operator+= (ItemType* pItem) { return (NULL!= pItem)? (_uItems.insert(_uItems.end(), pItem)): _uItems.end(); }

        
// operator-=, erase delegate from "pfn"
        delegate& operator-= (lpfnCommonFunc pfn) {    return (NULL!= pfn)? erase(int(pfn)): *this; }

        
// operator-=, erase all delegates from "pObject"
        template<typename ObjectType>
        
delegate& operator-= (ObjectType* pObject) { return (NULL!= pObject)? erase(int(pObject)): *this; }

        
// operator-=, erase delegate with iterator "position"
        delegate& operator-= (iterator position)
        {    
            delete 
*position;
            _uItems.erase(position);
            
return *this;
        }

        
// invoke functions delegated
        template<typename Param1>
        
void operator()(Param1 p1)
        {
            ItemPack::iterator iter
= _uItems.begin();
            
for (; _uItems.end()!= iter; ++iter)
            {
                (
*iter)->invoke(p1);
            }
        }

    
private:

        
// clear
        void clear(void)
        {
            
if (!_uItems.empty())
            {
                ItemPack::iterator iter
= _uItems.begin();
                
for (; _uItems.end()!= iter; ++iter)
                {
                    delete 
*iter;
                }

                _uItems.clear();
            }
        }

        
// erase all delegates of type "group"
        delegate& erase(int group)
        {
            assert(group
!= 0);
            
if (!_uItems.empty())
            {
                ItemPack::iterator iter
= _uItems.begin();
                
while(_uItems.end()!= iter)
                {
                    ItemType
* pItem= *iter;
                    
if (pItem->get_group()== group)
                    {
                        delete pItem;
                        _uItems.erase(iter
++);                        
                    }
                    
else
                    {
                        
++iter;
                    }
                }
            }

            
return *this;
        }

        
delegate(const delegate&);
        
delegate& operator=(const delegate&);

        ItemPack    _uItems;
    };
}
#endif // _LIB_DELEGATE_HPP_INCLUDED_

简单测试:

#include <cstdlib>
#include 
<iostream>
#include 
<string>
#include 
"delegate.hpp"

class Player
{
public:
    typedef lib::
delegate<boolconst std::string&>    ChangNameEvent;

    ChangNameEvent    NameChanging;
    ChangNameEvent    NameChanged;

public:
    
void SetName(const std::string& name)
    {
        
if (NameChanging)
        {
            NameChanging(name);
        }

        _name
= name;

        
if (NameChanged)
        {
            NameChanged(name);
        }
    }

    std::
string GetName(voidconst
    {
        
return _name;
    }

private:
    std::
string        _name;
};

class Game
{
public:
    Game(
void)
    {
        _player.NameChanging
+= lib::make_delegate(this&Game::_NameChanging);
        _player.NameChanged
+= lib::make_delegate(this&Game::_NameChanged);
    }

    
~Game(void)
    {
        _player.NameChanging
-= this;
        _player.NameChanged
-= this;
    }

    
void Test(void)
    {
        _player.SetName(
"python");
        _player.SetName(
"c++");
    }

private:
    
bool _NameChanging(const std::string& name)
    {
        std::cout
<<"current name= "<< _player.GetName()<< ", changing name= "<< name<< std::endl;
        
return true;
    }

    
bool _NameChanged(const std::string& name)
    {
        std::cout
<<"current name= "<< _player.GetName()<< ", changed name= "<< name<< std::endl;
        
return true;
    }

private:
    Player    _player;
};

int main(int argc, char* argv[])
{
    Game game;
    game.Test();

    system(
"pause");
    
return 0;
}


已知问题:

  1. 我见到网上很多实现代码都能以delegate<void(int)> deleg;的方式定义对象,这在VC6中似乎无法通过编译;

  2. 当前lib::delegate必须有且只有一个参数,也就是Param1,我现在没有想到好的办法可以在VC6下支持多参数或零参数的情况;

  3. 没有考虑函数对象、常量成员函数之类,目前为止尚无需求;

  4. 没有考虑多线程模型,目前为止尚无需求;

  5. 使用了std::list,是为了在删除某些委托类对象(item_base对象)client端代码保存的迭代器对象仍然有效,但同时也损失了效率;

  6. 在测试用例中,在game对象析构前要小心的将自己从挂接到的delegate对象中删除,这依赖于程序员的大脑;

  7. 仅用于VC6

posted on 2009-09-01 14:47 李现民 阅读(2209) 评论(18)  编辑 收藏 引用 所属分类: design

评论

# re: VC6中的简易delegate实现 2009-09-01 15:38 陈梓瀚(vczh)

template <typename T>
class delegate;

template <typename R>
class delegate<R(*)()>;

template <typename R, typename P1>
class delegate<R(*)(P1)>;

template <typename R, typename P1, typename P2>
class delegate<R(*)(P1, P2)>;

如果你要支持0-20个参数,那么写21个重载就好了。如果你不想写这么多,那么你写个程序去生成代码。如果你想支持成员函数,那么翻倍。如果你想支持functor,那么再翻倍。  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-01 16:09 戴尔电脑

不错啊!  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-01 16:47 李现民

@陈梓瀚(vczh)
这种写法在VC6中编译不过的,不知道有没有变通手法啊  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-01 17:28 LOGOS

喜欢用函数指针+void* arg的人飘过  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-01 20:11 陈梓瀚(vczh)

@李现民
十多年前的东西,别执着了,vc10 beta都有了  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-01 20:50 OwnWaterloo

@李现民
boost.function有3种使用语法:

1.
function<void ()> ...
function<void (int) > ...
function<int (std::string,int) > ...

2.
function<void> ...
function<void,int> ...
function<int ,std::string,int > ...

3.
function0<void> ...
function1<void,int> ...
function2<int ,std::string,int > ...

越往后, 语法越丑陋, 但受更多编译器支持。

可以试试在vc6上是否支持boost.function的2使用方法(第1种就不要指望了~~~)
如果行, 就看boost是怎么做的。

我估计后一种语法的实现依然要偏特化, 所以不行。
那就只能使用最丑陋的第3种语法了。  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-01 20:54 OwnWaterloo

@李现民
如果楼主打算试试boost.function on vc6, 请告知下结果哦~~~
我对vc6吐出的错误有些恐惧~~~  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-01 21:03 李现民

@陈梓瀚(vczh)
我也不想用啊,但我只是个兵啊,这不是没有办法么,呵呵  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-01 21:05 李现民

@OwnWaterloo
恐怕暂时不会去试boost了,其实我到想,但公司明确禁止在项目中使用boost。我曾经试过,被上司否定了  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-01 21:07 李现民

@OwnWaterloo
不过你说的那三种语法中的第三种,VC6肯定支持,呵呵,现在正在向第二种努力  回复  更多评论   

# re: VC6中的简易delegate实现[未登录] 2009-09-02 17:29 foxriver

不错哦,我已经加到自己的程序里的,谢谢楼主,呵呵。  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-02 17:39 李现民

@foxriver
呀,意外之喜,同样谢谢你,哈哈  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-02 21:27 空明流转

纯OO的办法,就是用接口通讯。。。  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-06 18:11 个性艺术签名

分享了~谢谢  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-07 07:11 free2000fly

http://www.codeproject.com/KB/cpp/FastDelegate.aspx
这个支持 vc6
  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-07 09:20 李现民

@free2000fly
这个是单播的、面向编译器的  回复  更多评论   

# re: VC6中的简易delegate实现 2009-09-10 17:23 tomlau

Maybe you like sigslot.  回复  更多评论   

# re: VC6中的简易delegate实现[未登录] 2013-04-01 15:52 JEFF

ALL ARE SIMPLE IN DELPHI OR CB !  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理