C++事件的实现

C++ 事件的实现
2005-12-30 baickl Beijin

简介

本文将向你演示如何在标准 C++ 中实现 C++Builder 中的 __closure ,以及 C# 中的 __delegate 机制,所以在看本文之前,请确定自己对这些机制已经有所了解。通过本文你将可以领略 C++ 语言的无限魅力以及他的深奥。 ( 注:非经授权,请勿请将此文用于商业活动)

原理探讨

在探讨之前,先看一下 C# 里的 __delegate 的用法,请看下例:
Delegate int SomeDelegate(string s, bool b); // 委托申明
rivate int SomeFunction(string str, bool bln){...} //某个类中的成员函数
SomeDelegate sd = new SomeDelegate(SomeFunction); //创建一个委托
sd(str,false); //由委托调用函数


上面部分为伪代码,相信接触过事件机构的人,都应该明白他的意思。由上面这段代码我们总结一下 C# 里委托的一些基本原理:
1.C# 中的委托是一个类,当你声明他的时候,就产生一个新的类型
2.C# 中的委托类创建的实例,可以通过函数的地址来初始化
3.C# 中的委托调用,实际上只是简单的调用被委托的函数


OK, 我们只需要了解这么多信息,如果有不对的地方,还请各位纠正。下面我们分析一下 C++ 中的事件的实现的困难度及原理。在 C++ 中我们实现的事件必须具备以下特性:
1. 事件所委托的函数的多样参数支持
2. 支持类成员函数类外部函数的委托
3.可以实现调用事件来达到调用委托函数的目的
上面的叙述,可能不是非常清楚,好吧,我就以具体的代码来说明这一切。我相信大部分技术人员还是不反感看代码的。在此做个声明,在C++中实现事件机构,绝对要用模板类来实现,除非你做的像Java里的事件机制,那我无话可说。
我们首先讨论第一个问题,这个问题是最难以被解决的。至少本人目前在网上所见到的事件机构,都只能支持一种参数。要么只有2个参数,要么只有3个参数,那我对这一块也做一些相关的讨论,以说明我的实现的原理。
其实在C++中,大家应该听过模板默认类型吧(听到这,某些人应该知道我所说的原理了),对,我就是用他来做对多参数的支持。看下面的声明:
struct null_event{};//声明一个空类型,以作默认参数使用


template
<
typename RT, //
返回值,以下是参数列表,注意,全部采用了默认参数
typename T0 = null_event, typename T1 = null_event, typename T2 = null_event,
typename T3 = null_event, typename T4 = null_event, typename T5 = null_event,
typename T6 = null_event, typename T7 = null_event, typename T8 = null_event,
typename T9 = null_event, typename T10 = null_event, typename T11 = null_event
>
class BaseEvent
{
public:


virtual~BaseEvent(){}
//以下为调用形式,我给出了13种调用形式,分别对应13种情况
virtual RT Invoke()=0;
virtual RT Invoke(T0)=0;
virtual RT Invoke(T0,T1)=0;
virtual RT Invoke(T0,T1,T2)=0;
virtual RT Invoke(T0,T1,T2,T3)=0;
virtual RT Invoke(T0,T1,T2,T3,T4)=0;
virtual RT Invoke(T0,T1,T2,T3,T4,T5)=0;
virtual RT Invoke(T0,T1,T2,T3,T4,T5,T6)=0;
virtual RT Invoke(T0,T1,T2,T3,T4,T5,T6,T7)=0;
virtual RT Invoke(T0,T1,T2,T3,T4,T5,T6,T7,T8)=0;
virtual RT Invoke(T0,T1,T2,T3,T4,T5,T6,T7,T9,T9)=0;
virtual RT Invoke(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10)=0;
virtual RT Invoke(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11)=0;
};
我们的事件基类虽然被创建出来了,但我们需要做的还很多,我们需要解决怎么将类成员函数和类外部函数同事件类相挂接。这一部分需要通过派生类来实现。首先我们看看类外部函数事件的实现:
template
<
typename RT,//
下面的形式是不是很熟悉?
typename T0 = null_event, typename T1 = null_event, typename T2 = null_event,
typename T3 = null_event, typename T4 = null_event, typename T5 = null_event,
typename T6 = null_event, typename T7 = null_event, typename T8 = null_event,
typename T9 = null_event,typename T10 = null_event,typename T11 = null_event
>
class NormalEvent:public BaseEvent<RT,T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11>
{
private:
//定义不同类型的函数,参数支持范围为0-12
typedef RT (*FUN0) ();
typedef RT (*FUN1) (T0);
typedef RT (*FUN2) (T0,T1);
typedef RT (*FUN3) (T0,T1,T2);
typedef RT (*FUN4) (T0,T1,T2,T3);
typedef RT (*FUN5) (T0,T1,T2,T3,T4);
typedef RT (*FUN6) (T0,T1,T2,T3,T4,T5);
typedef RT (*FUN7) (T0,T1,T2,T3,T4,T5,T6);
typedef RT (*FUN8) (T0,T1,T2,T3,T4,T5,T6,T7);
typedef RT (*FUN9) (T0,T1,T2,T3,T4,T5,T6,T7,T8);
typedef RT (*FUN10)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9);
typedef RT (*FUN11)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10);
typedef RT (*FUN12)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11);


private:
//创建函数的指针
FUN0 m_Fun0;
FUN1 m_Fun1;
FUN2 m_Fun2;
FUN3 m_Fun3;
FUN4 m_Fun4;
FUN5 m_Fun5;
FUN6 m_Fun6;
FUN7 m_Fun7;
FUN8 m_Fun8;
FUN9 m_Fun9;
FUN10 m_Fun10;
FUN11 m_Fun11;
FUN12 m_Fun12;
//默认初始化工作
void Default()
{
m_Fun0=0;
m_Fun1=0;
m_Fun2=0;
m_Fun3=0;
m_Fun4=0;
m_Fun5=0;
m_Fun6=0;
m_Fun7=0;
m_Fun8=0;
m_Fun9=0;
m_Fun10=0;
m_Fun11=0;
m_Fun12=0;
}


public:
//其实关键都在这些构造函数里,因为构造函数有13种,
//所以支持13种类型的外部函数被挂接到事件对象中
NormalEvent(FUN0 Fun)
{
assert(Fun!=0);


Default();
m_Fun0=Fun;
}


NormalEvent(FUN1 Fun)
{
assert(Fun!=0);


Default();
m_Fun1=Fun;
}


NormalEvent(FUN2 Fun)
{
assert(Fun!=0);


Default();
m_Fun2=Fun;
}
NormalEvent(FUN3 Fun)
{
assert(Fun!=0);


Default();
m_Fun3=Fun;
}
//被省略的部分。。。
NormalEvent(FUN12 Fun)
{
assert(Fun!=0);


Default();
m_Fun12=Fun;
}
//以下是不同形式的调用,取决于你的函数参数,不多做说明
virtual RT Invoke()//挂接无参函数的委托调用
{
assert(m_Fun0!=0);
return (*m_Fun0)();
}


virtual RT Invoke(T0 _0)//挂接一个参数函数的委托调用
{
assert(m_Fun1!=0);
return(*m_Fun1)(_0);
}


virtual RT Invoke(T0 _0 , T1 _1)//挂接两个参数函数的委托调用
{
assert(m_Fun2!=0);
return(*m_Fun2)(_0,_1);
}


.
. 省略一部分。。其实大家都知道
.


virtual RT Invoke(T0 _0 , T1 _1 , T2 _2 , T3 _3 , T4 _4 , T5 _5 , T6 _6 , T7 _7 , T8 _8 , T9 _9 , T10 _10 , T11 _11)
{
assert(m_Fun12!=0);
return(*m_Fun12)(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11);
}
};


到上面这一部分,应该比较明了了。因为原理已经很清楚,是不是有点像boost里的tuple实现机制,没错,就是他。对于类外部函数的调用就已经非常明了,我们还需要给几一些函数来做最基本的挂接工作,如下,先不说明为什么这么说,看到后面大家就会明白了。
template<typename RT>
BaseEvent<RT>*EventHandler(RT (*fun)())
{
return new NormalEvent<RT>(fun);
}

template<typename RT,typename T0>
BaseEvent<RT,T0>*EventHandler(RT (*fun)(T0))
{
return new NormalEvent<RT,T0>(fun);
}


template<typename RT,typename T0,typename T1>
BaseEvent<RT,T0,T1>*EventHandler(RT (*fun)(T0,T1))
{
return new NormalEvent<RT,T0,T1>(fun);
}
//以下省略..


上面是对类外部事件的完成,那么对类成员函数事件的原理也是一样的,我们仅贴出相关的代码不多做说明,在最后的时候,会介绍一个整合的类,用以完成对事件的更完整的描述及封装。
template
<
typename C,
typename RT,
typename T0 = null_event,typename T1 = null_event,typename T2 = null_event,typename T3 = null_event,
typename T4 = null_event,typename T5 = null_event,typename T6 = null_event,typename T7 = null_event,
typename T8 = null_event,typename T9 = null_event,typename T10 = null_event,typename T11 = null_event
>
class MemberEvent:public BaseEvent<RT,T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11>
{
private:


typedef RT (C::*FUN0) ();
typedef RT (C::*FUN1) (T0);
typedef RT (C::*FUN2) (T0,T1);
typedef RT (C::*FUN3) (T0,T1,T2);
typedef RT (C::*FUN4) (T0,T1,T2,T3);
typedef RT (C::*FUN5) (T0,T1,T2,T3,T4);
typedef RT (C::*FUN6) (T0,T1,T2,T3,T4,T5);
typedef RT (C::*FUN7) (T0,T1,T2,T3,T4,T5,T6);
typedef RT (C::*FUN8) (T0,T1,T2,T3,T4,T5,T6,T7);
typedef RT (C::*FUN9) (T0,T1,T2,T3,T4,T5,T6,T7,T8);
typedef RT (C::*FUN10)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9);
typedef RT (C::*FUN11)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10);
typedef RT (C::*FUN12)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11);


private:


FUN0 m_Fun0;
FUN1 m_Fun1;
FUN2 m_Fun2;
FUN3 m_Fun3;
FUN4 m_Fun4;
FUN5 m_Fun5;
FUN6 m_Fun6;
FUN7 m_Fun7;
FUN8 m_Fun8;
FUN9 m_Fun9;
FUN10 m_Fun10;
FUN11 m_Fun11;
FUN12 m_Fun12;


C *m_Object;


void Default()
{
m_Fun0=0;
m_Fun1=0;
m_Fun2=0;
m_Fun3=0;
m_Fun4=0;
m_Fun5=0;
m_Fun6=0;
m_Fun7=0;
m_Fun8=0;
m_Fun9=0;
m_Fun10=0;
m_Fun11=0;
m_Fun12=0;


m_Object=0;
}


public:


MemberEvent(C*pObject,FUN0 Fun)
{
assert(pObject!=0 && Fun!=0);


Default();
m_Fun0=Fun;
m_Object=pObject;
}


MemberEvent(C*pObject,FUN1 Fun)
{
assert(pObject!=0 && Fun!=0);


Default();
m_Fun1=Fun;
m_Object=pObject;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值