VC6中的简易delegate实现
版本:0.1
最后修改:2009-09-01
撰写:李现民
	其实原本不想自己写C++委托类的,因为CodeProject已经有许多相关讨论了,国内的前辈也写了很多,但经过一一试用后我无奈的发现它们无一例外的都使用了大量的template技巧,而一些像偏特化之类的template特性在VC6中并没有得到支持。于是只好自己动手了,虽然粗陋,但好在还勉强能胜任一些工作。非VC6平台的同学请参考别的实现。
	代码主要参考了jfwan的《一个C#的delegate在C++中的实现》一文:
#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(void) const= 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(void) const { 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(void) const { 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<bool, const 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(void) const
    {
        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;
}
已知问题:
    - 
    我见到网上很多实现代码都能以delegate<void(int)>
    deleg;的方式定义对象,这在VC6中似乎无法通过编译; 
- 
    当前lib::delegate必须有且只有一个参数,也就是Param1,我现在没有想到好的办法可以在VC6下支持多参数或零参数的情况; 
- 
    没有考虑函数对象、常量成员函数之类,目前为止尚无需求; 
- 
    没有考虑多线程模型,目前为止尚无需求; 
- 
    使用了std::list,是为了在删除某些委托类对象(item_base对象)时client端代码保存的迭代器对象仍然有效,但同时也损失了效率; 
- 
    在测试用例中,在game对象析构前要小心的将自己从挂接到的delegate对象中删除,这依赖于程序员的大脑; 
- 
    仅用于VC6;