signal oracle算法,我自己的signal / slot实现

www.codeproject.com 上又发现比较好玩的东西:http://www.codeproject.com/cpp/acfdelegate.asp

或者去 http://acfproj.sourceforge.net/.

作者甚是好耐心开发了一套和C#用法差不多的C++库。当然这里只是着重讨论他的Delegate实现。其实Delegate也不是什么新技术,正如下文有人提出的boost.singal (http://www.boost.org/doc/html/signals.html)、另外还有:

Delegate - Extended Callback Library    http://callbackext.berlios.de

Libsigc++                   http://libsigc.sourceforge.net

libSigCX(对libSigC++的扩展) http://libsigx.sourceforge.net

sigslot                        http://sigslot.sourceforge.net/

slotsig                        http://slotsig.sourceforge.net/

这个和上面的sigslot不是同一个东西,作者起的名字……一点新意都没有,而且作者还提供了一个性能比较的benchmark,比较了qt、boost、libsigc++和自己,结果可以看这里:http://slotsig.sourceforge.net/doc/benchmarks.html

作者还说:The sigslot library has not been benchmarked, because it provides too less features than the others。当然Qt用的是完全另外一套东西,通过预编译实现的signal / slot,个人觉得完全没有可比性,当然Qt的实现方法也是我个人所讨厌的,非要多一个预编译器出来,开发时候极为不爽。

找回以前的代码,我在2002年的时候原来也已经实现过一个signal / slot,不过在slotsig作者眼中也是provides too less features。不过当然是自己够用就行了。之所以要“重复开发”,原因之一是以上的很多lib不支持VC6,当时我还在用破破烂烂的VC6;一方面我也不愿意自己的project里面塞一大堆lib,特别是boost的,庞大的吓人;一方面也就是自己能力的锻炼;还有一方面就是发现有些lib里面的实现用了些trick(例如用byte array保存指针),这也是个人不喜欢的东西。

其实实现的原理很简单,先以一个简单的例子说明:

假设需要处理   int    f(int a);的singl / slot,如果单是函数指针,那就太简单了,slot就是函数指针,singal就是函数指针的数组。但现在还要加上对象的成员函数,于是就会想到把这两种东西合并起来,但从外面看有相同的接口。其实function object就是这样一种东西,不过function object只能在编译时期实现普通函数和成员函数接口统一,但现在需要在运行时期,很自然就会想到了虚函数:

struct slot1base

{ virtual int fun(int a) = 0; };

typedef  SmartPotint   slot1;

struct singal1

{

int emit(int a) {    /* call each slot in m_listSlot */ }

void connect( slot1 s) { m_listSlot.push_back(s); }

protected:

std::list   m_listSlot;

};

其中SmartPoint就是带引用计数的智能指针,很多实现的库,随便选一个吧。

很明显,我们现在就是需要一个singal1,然后往它里面的m_listSlot里面不断放slot1base的子类。现在要做的事情,就是要把不同的形如 int f(int)样式的函数调用(包括普通函数和成员函数)统统转换为slot1base的子类。于是就有很多子类出来了:

struct slot1func : public slot1base

{

typedef int (* func)(int);

slot1func(func p) : m_func(p) {}

virtual int fun(int a) { return m_func(a); }

private:

func m_func;

};

template

struct slot1objfunc : public slot1base

{

typedef int (obj::*func)(int);

slot1objfunc(obj * p, func pF) : m_obj(p),m_pfunc(pF) {}

virtual int fun(int a) { return (m_obj->*m_func)(a); }

private:

obj * m_obj;

func m_pfunc;

};

其实除了这两种以外,还可以扩展出更多的子类,例如能自动进行函数参数的类型转换的子类:

struct slot1func_for_short : public slot1base

{

typedef short (* func)(short);

slot1func_for_short(func p) : m_func(p) {}

virtual int fun(int a) { return m_func(a); }

private:

func m_func;

};

当然可以抽象为template

template

struct slot1func_for_other : public slot1base

{

typedef R2 (* func)(A2);

slot1func_for_other(func p) : m_func(p) {}

virtual int fun(int a) { return m_func(a); }

private:

func m_func;

};

同样还应该有for object function的版本……

除了这些,还可以有更多的,例如处理object的const函数的;处理本来没有返回值,每次需要虚拟一个返回值的函数;参数个数不一致,需要另外bind参数的……

现在有了很多子类以后,我们当然不希望要记着这么多子类的名称,希望使用的时候只通过同一个接口就可以生成我们所需要的slot1,这时候就是经典的template function出场了:

slot1 slot(int (*func)(int))

{ return slot1( new slot1func(func) ); }

template

slot2 slot(obj * p, int (obj::*func)(int) )

{ return slot1( new slot1objfunc(p,func) ); }

template

slot2 slot( R2 (*func)(A2) )

{ return slot1( new slot1func_for_other(func) ); }

应用的时候就可以:

int    func1(int a) { ..... }

struct MyObj {    int func2(int a) { .... } };

MyObj     obj;

signal1      onButtonClick;

onButtonClick.connect( slot( func1) );

onButtonClick.connect( slot( *obj, func2) );

return onButtonClick.emit( 100 );

原理很简单吧,但这个只是int func(int)格式的slot1,如果参数不是int,返回值不是int呢?简单,把上面的东西变成模版就行了。

template

struct slot1base

{ virtual R fun(A1 a) = 0; };

template

struct slot1 : public  SmartPotint >   {}

template

struct singal1

{

R emit(A a) {    /* call each slot in m_listSlot */ }

void connect( slot1 s) { m_listSlot.push_back(s); }

protected:

std::list >   m_listSlot;

};

……后面的子类也作相应的处理。

原理是简单,不过要做一个完整的机制还要考虑更多的东西:

1、singal 和slot之间一般是“多对多”的关系,所以应该slot里面也有singal的列表,以方便双向查找。

2、多线程下的重入问题,如何加上锁,是需要仔细考虑的。

3、当一个singal里面包含多个slot的时候,那个返回值的处理,这个真的是多种多样了:

只要其中一个的返回值;所有返回值记录在数组里面;当其中一个返回值是特定值时候不继续后面的slot……

所以一般都会在singal1的模版参数中增加一个TMarshal的类来处理返回值:

template

struct singal1

{

R emit(A1 a)

{

typeMarsh marsh;

/* for each item in m_listSlot*/

{

if (! marsh.toContinue( (*it)->fun(a)))

break;

}

return marsh.value();

}

};

而一个简单TMarshal可以是这样:

template

class TMarshal

{

public:

TMarshal(void) : m_saveValue() {}

static typeReturn defaultValue(void) { return typeReturn(); }

typeReturn value(void) { return m_saveValue; }

bool toContinue(const typeReturn & val)

{ m_saveValue = val; return true; }

protected:

typeReturn m_saveValue;

};

4、现在只是讨论了一个参数的情况,多个参数的呢?好办,copy/paste一个参数的,加上多个参数就是了,例如:

template

struct slot2base

{  virtual R fun(A1 a1, A2 a2) = 0; };

不过的确是很烦人的工作,于是很多lib都是通过宏来实现,例如:

一个 signalslot.imp的文件里面:

template

struct SLOTBASENAME

{ virtual R fun(FUNC_ARGS) = 0; };

……

而singalslot.h里面就定义:

#define TEMPLATE_ARGS typename A1

#define FUNC_ARGS  A1 a1

#define SLOTBASENAME slot1base

#include "signalslot.imp"

#define TEMPLATE_ARGS typename A1,typename A2

#define FUNC_ARGS  A1 a1, A2 a2

#define SLOTBASENAME slot2base

#include "signalslot.imp"

……

更有甚者,直接用perl来生成.h文件……

5、需要更多的接口,方便使用,例如slot的查找、删除、比较之类的。

posted on 2005-10-08 14:50 cyt 阅读(5046) 评论(2)  编辑 收藏 引用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值