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;