原文地址:点击打开链接
说明
委托和事件绝对是 .NET/C#中很酷的特性,而在标准C++上已存在很多对其模拟的尝试。本文中基于标准C++的委托类试图提供一个完整并易于使用的解决方案。
设计目标
在CodeProject中已存在几个很好的委托类实现。然而,仍然没有足够完美的方案应对广泛的开始使用。比如,一些实现中并未支持多播委托,一些不支持函数对象,而还有一些实现在应用中难以使用。所以本文的委托类的设计目标为:
*支持所有C++可调用对象,包含静态函数,成员函数以及函数对象。
*在同一个委托类中支持单播和多播委托,使使用者不必自己实现多播代理。(多播委托其调用列表中可以拥有多个元素的委托)
*易于理解与使用。
例子
1、使用委托
#include "AcfDelegate.h"
using namespace Acf;
static void H() { ... }
class Foo {
public:
void G() { ... }
};
class Foo2 {
public:
void operator()(int n) { ... }
};
// Create delegate a which attaches to a static function
Delegate<void ()> a(&H);
assert(a == &H);
// Create delegate b which attaches
// to an instance and a member function
Foo foo;
Delegate<void ()> b(&foo, &Foo::G);
assert(b == std::make_pair(&foo, &Foo::G);
// Create delegate c from a
Delegate<void ()> c = a;
assert(c == &H);
// Create delegate d which attaches to a function object
Delegate<void (int)> d(Foo2());
// Call delegates
a();
d(100);
// Combine/remove delegates
d += &H;
d += std::make_pair(&foo, &Foo::G);
d -= std::make_pair(&foo, &Foo::G);
d -= &H;
2、使用事件
class Button {
public:
Delegate<void ()> Click;
};
Button btn;
btn.Click += &F;
btn.Click += std::make_pair(&o, &MyObj::G);
btn.Click();
btn.Click -= std::make_pair(&o, &MyObj::G);
3、使用事件(高级)
当你需要更多的管理事件,比如你所关心的线程安全性。
class Button {
private:
Delegate<void ()> click;
Mutex mutex;
public:
template <class T>
void add_Click(const T& h) {
ScopedLock lock(this->mutex);
this->click += h;
}
template <class T>
void remove_Click(const T& h) {
ScopedLock lock(this->mutex);
this->click -= h;
}
protected:
void OnClick() { if (this->click) this->click(); }
};
btn.add_Click(&F);
btn.add_Click(std::make_pair(&o, &MyObj::G));
namespace Acf {
template <class TSignature>
class Delegate; // no body
template <class R, class T1, class T2, ..., class TN>
class Delegate<R (T1, T2, ..., TN)> {
// Constructor/Destructor
public:
Delegate();
template <class TFunctor>
Delegate(const TFunctor& f);
template <class TPtr, class TFunctionPtr>
Delegate(const TPtr& obj, const TFunctionPtr& mfp);
Delegate(const Delegate& d);
~Delegate();
// Properties
public:
bool IsEmpty() const;
bool IsMulticast() const;
// Methods
public:
template <class TFunctor>
void Add(const TFunctor& f);
template <class TPtr, class TFunctionPtr>
void Add(const TPtr& obj, const TFunctionPtr& mfp);
template <class TFunctor>
bool Remove(const TFunctor& f);
template <class TPtr, class TFunctionPtr>
bool Remove(const TPtr& obj, const TFunctionPtr& mfp);
void Clear();
// Operators
public:
operator bool() const;
bool operator!() const;
template <class TFunctor>
Delegate& operator=(const TFunctor& f);
Delegate& operator=(const Delegate& d);
template <class TFunctor>
Delegate& operator+=(const TFunctor& f);
template <class TFunctor>
friend Delegate operator+(const Delegate& d, const TFunctor& f);
template <class TFunctor>
friend Delegate operator+(const TFunctor& f, const Delegate& d);
template <class TFunctor>
Delegate& operator-=(const TFunctor& f);
template <class TFunctor>
Delegate operator-(const TFunctor& f) const;
template <class TFunctor>
friend bool operator==(const Delegate& d, const TFunctor& f);
template <class TFunctor>
friend bool operator==(const TFunctor& f, const Delegate& d);
template <class TFunctor>
friend bool operator!=(const Delegate& d, const TFunctor& f);
template <class TFunctor>
friend bool operator!=(const TFunctor& f, const Delegate& d);
R operator()(T1, T2, ..., TN) const;
};
} // namespace Acf
委托类中,TFunctor模板参数支持静态函数,成员函数(通过std::pair)以及函数对象等可调用对象。
TPtr模板参数支持普通指针(如Foo*)和智能指针(如boost::shared_ptr<Foo>).
注意
*在使用委托类的时,必须保证在你工程中支持RTTI
*委托类支持六个函数参数
源代码:
AcfDelegate.h
//-------------------------------------------------------------------------
//
// Copyright (C) 2004-2005 Yingle Jia
//
// Permission to copy, use, modify, sell and distribute this software is
// granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied warranty,
// and with no claim as to its suitability for any purpose.
//
// AcfDelegate.h
//
#ifndef __Acf_Delegate__
#define __Acf_Delegate__
#include <stdexcept> // for std::logic_error
#include <utility> // for std::pair
// Macros for template metaprogramming
#define ACF_JOIN(a, b) ACF_DO_JOIN(a, b)
#define ACF_DO_JOIN(a, b) ACF_DO_JOIN2(a, b)
#define ACF_DO_JOIN2(a, b) a##b
#define ACF_MAKE_PARAMS1_0(t)
#define ACF_MAKE_PARAMS1_1(t) t##1
#define ACF_MAKE_PARAMS1_2(t) t##1, ##t##2
#define ACF_MAKE_PARAMS1_3(t) t##1, ##t##2, ##t##3
#define ACF_MAKE_PARAMS1_4(t) t##1, ##t##2, ##t##3, ##t##4
#define ACF_MAKE_PARAMS1_5(t) t##1, ##t##2, ##t##3, ##t##4, ##t##5
#define ACF_MAKE_PARAMS1_6(t) t##1, ##t##2, ##t##3, ##t##4, ##t##5, ##t##6
#define ACF_MAKE_PARAMS2_0(t1, t2)
#define ACF_MAKE_PARAMS2_1(t1, t2) t1##1 t2##1
#define ACF_MAKE_PARAMS2_2(t1, t2) t1##1 t2##1, t1##2 t2##2
#define ACF_MAKE_PARAMS2_3(t1, t2) t1##1 t2##1, t1##2 t2##2, t1##3 t2##3
#define ACF_MAKE_PARAMS2_4(t1, t2) t1##1 t2##1, t1##2 t2##2, t1##3 t2##3, t1##4 t2##4
#define ACF_MAKE_PARAMS2_5(t1, t2) t1##1 t2##1, t1##2 t2##2, t1##3 t2##3, t1##4 t2##4, t1##5 t2##5
#define ACF_MAKE_PARAMS2_6(t1, t2) t1##1 t2##1, t1##2 t2##2, t1##3 t2##3, t1##4 t2##4, t1##5 t2##5, t1##6 t2##6
#define ACF_MAKE_PARAMS1(n, t) ACF_JOIN(ACF_MAKE_PARAMS1_, n) (t)
#define ACF_MAKE_PARAMS2(n, t1, t2) ACF_JOIN(ACF_MAKE_PARAMS2_, n) (t1, t2)
namespace Acf {
class InvalidCallException : public std::logic_error
{
public:
InvalidCallException() : std::logic_error("An empty delegate is called")
{
}
};
template <class T>
inline static T _HandleInvalidCall()
{
throw InvalidCallException();
}
template <>
inline static void _HandleInvalidCall<void>()
{
}
template <class TSignature>
class Delegate; // no body
} // namespace Acf
// Specializations
#define ACF_DELEGATE_NUM_ARGS 0 // Delegate<R ()>
#include "AcfDelegateTemplate.h"
#undef ACF_DELEGATE_NUM_ARGS
#define ACF_DELEGATE_NUM_ARGS 1 // Delegate<R (T1)>
#include "AcfDelegateTemplate.h"
#undef ACF_DELEGATE_NUM_ARGS
#define ACF_DELEGATE_NUM_ARGS 2 // Delegate<R (T1, T2)>
#include "AcfDelegateTemplate.h"
#undef ACF_DELEGATE_NUM_ARGS
#define ACF_DELEGATE_NUM_ARGS 3 // Delegate<R (T1, T2, T3)>
#include "AcfDelegateTemplate.h"
#undef ACF_DELEGATE_NUM_ARGS
#define ACF_DELEGATE_NUM_ARGS 4 // Delegate<R (T1, T2, T3, T4)>
#include "AcfDelegateTemplate.h"
#undef ACF_DELEGATE_NUM_ARGS
#define ACF_DELEGATE_NUM_ARGS 5 // Delegate<R (T1, T2, T3, T4, T5)>
#include "AcfDelegateTemplate.h"
#undef ACF_DELEGATE_NUM_ARGS
#define ACF_DELEGATE_NUM_ARGS 6 // Delegate<R (T1, T2, T3, T4, T5, T6)>
#include "AcfDelegateTemplate.h"
#undef ACF_DELEGATE_NUM_ARGS
#endif // #ifndef __Acf_Delegate__
AcfDelegateTemplate.h
//-------------------------------------------------------------------------
//
// Copyright (C) 2004-2005 Yingle Jia
//
// Permission to copy, use, modify, sell and distribute this software is
// granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied warranty,
// and with no claim as to its suitability for any purpose.
//
// AcfDelegateTemplate.h
//
// Note: this header is a header template and must NOT have multiple-inclusion
// protection.
#define ACF_DELEGATE_TEMPLATE_PARAMS ACF_MAKE_PARAMS1(ACF_DELEGATE_NUM_ARGS, class T)
// class T0, class T1, class T2, ...
#define ACF_DELEGATE_TEMPLATE_ARGS ACF_MAKE_PARAMS1(ACF_DELEGATE_NUM_ARGS, T)
// T0, T1, T2, ...
#define ACF_DELEGATE_FUNCTION_PARAMS ACF_MAKE_PARAMS2(ACF_DELEGATE_NUM_ARGS, T, a)
// T0 a0, T1 a1, T2 a2, ...
#define ACF_DELEGATE_FUNCTION_ARGS ACF_MAKE_PARAMS1(ACF_DELEGATE_NUM_ARGS, a)
// a0, a1, a2, ...
// Comma if nonzero number of arguments
#if ACF_DELEGATE_NUM_ARGS == 0
#define ACF_DELEGATE_COMMA
#else
#define ACF_DELEGATE_COMMA ,
#endif
namespace Acf {
//-------------------------------------------------------------------------
// class Delegate<R (T1, T2, ..., TN)>
template <class R ACF_DELEGATE_COMMA ACF_DELEGATE_TEMPLATE_PARAMS>
class Delegate<R (ACF_DELEGATE_TEMPLATE_ARGS)>
{
// Declaractions
private:
class DelegateImplBase
{
// Fields
public:
DelegateImplBase* Previous; // singly-linked list
// Constructor/Destructor
protected:
DelegateImplBase() : Previous(NULL) { }
DelegateImplBase(const DelegateImplBase& other) : Previous(NULL) { }
public:
virtual ~DelegateImplBase() { }
// Methods
public:
virtual DelegateImplBase* Clone() const = 0;
virtual R Invoke(ACF_DELEGATE_FUNCTION_PARAMS) const = 0;
};
template <class TFunctor>
struct Invoker
{
static R Invoke(const TFunctor& f ACF_DELEGATE_COMMA ACF_DELEGATE_FUNCTION_PARAMS)
{
return (const_cast<TFunctor&>(f))(ACF_DELEGATE_FUNCTION_ARGS);
}
};
template <class TPtr, class TFunctionPtr>
struct Invoker<std::pair<TPtr, TFunctionPtr> >
{
static R Invoke(const std::pair<TPtr, TFunctionPtr>& mf ACF_DELEGATE_COMMA ACF_DELEGATE_FUNCTION_PARAMS)
{
return ((*mf.first).*mf.second)(ACF_DELEGATE_FUNCTION_ARGS);
}
};
template <class TFunctor>
class DelegateImpl : public DelegateImplBase
{
// Fields
public:
TFunctor Functor;
// Constructor
public:
DelegateImpl(const TFunctor& f) : Functor(f)
{
}
DelegateImpl(const DelegateImpl& other) : Functor(other.Functor)
{
}
// Methods
public:
virtual DelegateImplBase* Clone() const
{
return new DelegateImpl(*this);
}
virtual R Invoke(ACF_DELEGATE_FUNCTION_PARAMS) const
{
return Invoker<TFunctor>::Invoke(this->Functor ACF_DELEGATE_COMMA ACF_DELEGATE_FUNCTION_ARGS);
}
};
// Fields
private:
DelegateImplBase* _last;
// Constructor/Destructor
public:
Delegate()
{
this->_last = NULL;
}
template <class TFunctor>
Delegate(const TFunctor& f)
{
this->_last = NULL;
*this = f;
}
template<class TPtr, class TFunctionPtr>
Delegate(const TPtr& obj, const TFunctionPtr& mfp)
{
this->_last = NULL;
*this = std::make_pair(obj, mfp);
}
Delegate(const Delegate& d)
{
this->_last = NULL;
*this = d;
}
~Delegate()
{
Clear();
}
// Properties
public:
bool IsEmpty() const
{
return (this->_last == NULL);
}
bool IsMulticast() const
{
return (this->_last != NULL && this->_last->Previous != NULL);
}
// Static Methods
private:
static DelegateImplBase* CloneDelegateList(DelegateImplBase* list, /*out*/ DelegateImplBase** first)
{
DelegateImplBase* list2 = list;
DelegateImplBase* newList = NULL;
DelegateImplBase** pp = &newList;
DelegateImplBase* temp = NULL;
try
{
while (list2 != NULL)
{
temp = list2->Clone();
*pp = temp;
pp = &temp->Previous;
list2 = list2->Previous;
}
}
catch (...)
{
FreeDelegateList(newList);
throw;
}
if (first != NULL)
*first = temp;
return newList;
}
static void FreeDelegateList(DelegateImplBase* list)
{
DelegateImplBase* temp = NULL;
while (list != NULL)
{
temp = list->Previous;
delete list;
list = temp;
}
}
static void InvokeDelegateList(DelegateImplBase* list ACF_DELEGATE_COMMA ACF_DELEGATE_FUNCTION_PARAMS)
{
if (list != NULL)
{
if (list->Previous != NULL)
InvokeDelegateList(list->Previous ACF_DELEGATE_COMMA ACF_DELEGATE_FUNCTION_ARGS);
list->Invoke(ACF_DELEGATE_FUNCTION_ARGS);
}
}
// Methods
public:
template <class TFunctor>
void Add(const TFunctor& f)
{
DelegateImplBase* d = new DelegateImpl<TFunctor>(f);
d->Previous = this->_last;
this->_last = d;
}
template<class TPtr, class TFunctionPtr>
void Add(const TPtr& obj, const TFunctionPtr& mfp)
{
DelegateImplBase* d = new DelegateImpl<std::pair<TPtr, TFunctionPtr> >(std::make_pair(obj, mfp));
d->Previous = this->_last;
this->_last = d;
}
template <class TFunctor>
bool Remove(const TFunctor& f)
{
DelegateImplBase* d = this->_last;
DelegateImplBase** pp = &this->_last;
DelegateImpl<TFunctor>* impl = NULL;
while (d != NULL)
{
impl = dynamic_cast<DelegateImpl<TFunctor>*>(d);
if (impl != NULL && impl->Functor == f)
{
*pp = d->Previous;
delete impl;
return true;
}
pp = &d->Previous;
d = d->Previous;
}
return false;
}
template<class TPtr, class TFunctionPtr>
bool Remove(const TPtr& obj, const TFunctionPtr& mfp)
{
return Remove(std::make_pair(obj, mfp));
}
void Clear()
{
FreeDelegateList(this->_last);
this->_last = NULL;
}
private:
template <class TFunctor>
bool Equals(const TFunctor& f) const
{
if (this->_last == NULL || this->_last->Previous != NULL)
return false;
DelegateImpl<TFunctor>* impl =
dynamic_cast<DelegateImpl<TFunctor>*>(this->_last);
if (impl == NULL)
return false;
return (impl->Functor == f);
}
// Operators
public:
operator bool() const
{
return !IsEmpty();
}
bool operator!() const
{
return IsEmpty();
}
template <class TFunctor>
Delegate& operator=(const TFunctor& f)
{
DelegateImplBase* d = new DelegateImpl<TFunctor>(f);
FreeDelegateList(this->_last);
this->_last = d;
return *this;
}
Delegate& operator=(const Delegate& d)
{
if (this != &d)
{
DelegateImplBase* list = CloneDelegateList(d._last, NULL);
FreeDelegateList(this->_last);
this->_last = list;
}
return *this;
}
template <class TFunctor>
Delegate& operator+=(const TFunctor& f)
{
Add(f);
return *this;
}
template <class TFunctor>
friend Delegate operator+(const Delegate& d, const TFunctor& f)
{
return (Delegate(d) += f);
}
template <class TFunctor>
friend Delegate operator+(const TFunctor& f, const Delegate& d)
{
return (d + f);
}
template <class TFunctor>
Delegate& operator-=(const TFunctor& f)
{
Remove(f);
return *this;
}
template <class TFunctor>
Delegate operator-(const TFunctor& f) const
{
return (Delegate(*this) -= f);
}
template <class TFunctor>
friend bool operator==(const Delegate& d, const TFunctor& f)
{
return d.Equals(f);
}
template <class TFunctor>
friend bool operator==(const TFunctor& f, const Delegate& d)
{
return (d == f);
}
template <class TFunctor>
friend bool operator!=(const Delegate& d, const TFunctor& f)
{
return !(d == f);
}
template <class TFunctor>
friend bool operator!=(const TFunctor& f, const Delegate& d)
{
return (d != f);
}
R operator()(ACF_DELEGATE_FUNCTION_PARAMS) const
{
if (this->_last == NULL)
return _HandleInvalidCall<R>();
if (this->_last->Previous != NULL)
InvokeDelegateList(this->_last->Previous ACF_DELEGATE_COMMA ACF_DELEGATE_FUNCTION_ARGS);
return this->_last->Invoke(ACF_DELEGATE_FUNCTION_ARGS);
}
};
} // namespace Acf
#undef ACF_DELEGATE_TEMPLATE_PARAMS
#undef ACF_DELEGATE_TEMPLATE_ARGS
#undef ACF_DELEGATE_FUNCTION_PARAMS
#undef ACF_DELEGATE_FUNCTION_ARGS
#undef ACF_DELEGATE_COMMA
Delegate.cpp
// Delegate.cpp : Defines the entry point for the console application.
//
#define CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#ifdef _MSC_VER
#include <crtdbg.h>
#endif // #ifdef _MSC_VER
#include "AcfDelegate.h"
using namespace Acf;
static void H(const char* s)
{
printf(" Static function invoked by %s\n", s);
}
class MyObject
{
public:
int count;
MyObject(int n) : count(n) { }
void G(const char* s)
{
printf(" Member function invoked by %s\n", s);
this->count++;
}
};
class MyFunctor
{
public:
void operator()(const char* s)
{
printf(" Functor invoked by %s\n", s);
}
};
int main(int argc, char* argv[])
{
#ifdef _MSC_VER
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif // #ifdef _MSC_VER
// Create an empty delegate
printf("Invoking delegate a0:\n");
Delegate<void ()> a0;
a0();
printf("\n");
// Create delegate a that references a static function:
Delegate<void (const char*)> a(&H);
assert(a == &H);
printf("Invoking delegate a:\n");
a("a");
printf("\n");
// Create delegate b that references an instance function:
MyObject myObj(0);
Delegate<void (const char*)> b(&myObj, &MyObject::G);
assert(b == std::make_pair(&myObj, &MyObject::G));
printf("Invoking delegate b:\n");
b("b");
assert(myObj.count == 1);
printf("\n");
// Create delegate c that references a function object:
MyFunctor myFunctor;
Delegate<void (const char*)> c(myFunctor);
printf("Invoking delegate c:\n");
c("c");
printf("\n");
// Add an instance function and a functor to delegate a
a += std::make_pair(&myObj, &MyObject::G);
a += MyFunctor();
printf("Invoking delegate a:\n");
a("a");
assert(myObj.count == 2);
printf("\n");
// Remove the static function from delegate a
a -= &H;
printf("Invoking delegate a:\n");
a("a");
assert(myObj.count == 3);
printf("\n");
// Create delegate d from a
Delegate<void (const char*)> d(a);
// Add delegate b to delegate d
d += Delegate<void (const char*)>(&H);
printf("Invoking delegate d:\n");
d("d");
assert(myObj.count == 4);
printf("\n");
getchar();
return 0;
}