织梦CMS - 轻松建站从此开始!

罗索

我自己的signal / slot实现

落鹤生 发布于 2012-05-22 09:21 点击:次 
Thesigslot libraryhas not been benchmarked, because it provides too less features than the others。当然Qt用的是完全另外一套东西,通过预编译实现的signal / slot,个人觉得完全没有可比性,当然Qt的实现方法也是我个人所讨厌的,非要多一个预编译器出来,开发时
TAG:

发现原来已经有两个多月没有写blog了。近来工作都很忙,回到家都懒懒的不想动。虽然工作上做了很多有趣的东西,但毕竟有版权保护,在公司做的工作不能直接写在blog上,也要抽些时间整理一下才能放上来。
今天在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
作者还说:Thesigslot libraryhas 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只能在编译时期实现普通函数和成员函数接口统一,但现在需要在运行时期,很自然就会想到了虚函数:

  1. struct slot1base 
  2.    { virtual int fun(int a) = 0; }; 
  3.  
  4. typedef  SmartPotint   slot1; 
  5.  
  6. struct singal1 
  7.     int emit(int a) {    /* call each slot in m_listSlot */ } 
  8.     void connect( slot1 s) { m_listSlot.push_back(s); } 
  9.  
  10. protected
  11.     std::list   m_listSlot; 
  12. }; 

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

  1. struct slot1func : public slot1base 
  2.  typedef int (* func)(int); 
  3.   
  4.  slot1func(func p) : m_func(p) {} 
  5.  virtual int fun(int a) { return m_func(a); } 
  6. private
  7.  func m_func; 
  8. }; 
  9.  
  10. template 
  11. struct slot1objfunc : public slot1base 
  12.  typedef int (obj::*func)(int); 
  13.   
  14.  slot1objfunc(obj * p, func pF) : m_obj(p),m_pfunc(pF) {} 
  15.  virtual int fun(int a) { return (m_obj->*m_func)(a); } 
  16. private
  17.  obj * m_obj; 
  18.  func m_pfunc; 
  19. }; 

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

  1. struct slot1func_for_short : public slot1base 
  2.  typedef short (* func)(short); 
  3.   
  4.  slot1func_for_short(func p) : m_func(p) {} 
  5.  virtual int fun(int a) { return m_func(a); } 
  6. private
  7.  func m_func; 
  8. }; 

当然可以抽象为template

  1. template 
  2. struct slot1func_for_other : public slot1base 
  3.  typedef R2 (* func)(A2); 
  4.   
  5.  slot1func_for_other(func p) : m_func(p) {} 
  6.  virtual int fun(int a) { return m_func(a); } 
  7. private
  8.  func m_func; 
  9. }; 

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

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

  1. slot1 slot(int (*func)(int)) 
  2.  { return slot1( new slot1func(func) ); } 
  3. template 
  4.  slot2 slot(obj * p, int (obj::*func)(int) ) 
  5.   { return slot1( new slot1objfunc(p,func) ); } 
  6. template 
  7.  slot2 slot( R2 (*func)(A2) ) 
  8.   { return slot1( new slot1func_for_other(func) ); } 

应用的时候就可以:

  1. int    func1(int a) { ..... } 
  2. struct MyObj {    int func2(int a) { .... } }; 
  3.  
  4. MyObj     obj; 
  5.  
  6. signal1      onButtonClick; 
  7. onButtonClick.connect( slot( func1) ); 
  8. onButtonClick.connect( slot( *obj, func2) ); 
  9. return onButtonClick.emit( 100 ); 

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

  1. template 
  2. struct slot1base 
  3.    { virtual R fun(A1 a) = 0; }; 
  4.  
  5. template 
  6. struct slot1 : public  SmartPotint >   {} 
  7.  
  8. template 
  9. struct singal1 
  10.     R emit(A a) {    /* call each slot in m_listSlot */ } 
  11.     void connect( slot1 s) { m_listSlot.push_back(s); } 
  12.  
  13. protected
  14.     std::list >   m_listSlot; 
  15. }; 

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

原理是简单,不过要做一个完整的机制还要考虑更多的东西:
1、singal 和slot之间一般是“多对多”的关系,所以应该slot里面也有singal的列表,以方便双向查找。
2、多线程下的重入问题,如何加上锁,是需要仔细考虑的。
3、当一个singal里面包含多个slot的时候,那个返回值的处理,这个真的是多种多样了

     只要其中一个的返回值;所有返回值记录在数组里面;当其中一个返回值是特定值时候不继续后面的slot……
     所以一般都会在singal1的模版参数中增加一个TMarshal的类来处理返回值:

  1. template 
  2.      struct singal1 
  3.      { 
  4.          R emit(A1 a) 
  5.          { 
  6.             typeMarsh marsh; 
  7.             /* for each item in m_listSlot*/ 
  8.             { 
  9.                  if (! marsh.toContinue( (*it)->fun(a))) 
  10.                       break
  11.             } 
  12.             return marsh.value(); 
  13.          } 
  14.     }; 

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

  1. template 
  2.  class TMarshal 
  3.  { 
  4.  public
  5.   TMarshal(void) : m_saveValue() {} 
  6.   static typeReturn defaultValue(void) { return typeReturn(); } 
  7.   typeReturn value(void) { return m_saveValue; } 
  8.   bool toContinue(const typeReturn & val) 
  9.    { m_saveValue = val; return true; } 
  10.  protected
  11.   typeReturn m_saveValue; 
  12.  }; 

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

  1. template 
  2.  struct slot2base 
  3.   {  virtual R fun(A1 a1, A2 a2) = 0; }; 

 不过的确是很烦人的工作,于是很多lib都是通过宏来实现,例如:
 一个 signalslot.imp的文件里面:

  1. template 
  2.   struct SLOTBASENAME 
  3.    { virtual R fun(FUNC_ARGS) = 0; }; 
  4.  …… 

 而singalslot.h里面就定义:

  1. #define TEMPLATE_ARGS typename A1 
  2. #define FUNC_ARGS  A1 a1 
  3. #define SLOTBASENAME slot1base 
  4. #include "signalslot.imp" 
  5.  
  6. #define TEMPLATE_ARGS typename A1,typename A2 
  7. #define FUNC_ARGS  A1 a1, A2 a2 
  8. #define SLOTBASENAME slot2base 
  9. #include "signalslot.imp" 
  10. …… 

 更有甚者,直接用perl来生成.h文件……
5、需要更多的接口,方便使用,例如slot的查找、删除、比较之类的。

(ilovejoy)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201205/16027.html]
本文出处:百度博客 作者:ilovejoy 原文
顶一下
(1)
100%
踩一下
(0)
0%
------分隔线----------------------------
相关文章
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容