现在的位置: 首页 > 综合 > 正文

libjingle源码分析之一:Signal机制

2013年02月01日 ⁄ 综合 ⁄ 共 3559字 ⁄ 字号 评论关闭
  • 摘要

        本文主要分析了libjingle中的Signal(信号)机制,它实际上是基于sigslot开源库。本文开始描述了Signal机制是什么;然后,给出一个libjingle文档中的例子,来描述它是如何使用的。最后,介绍了Signal机制的具体实现。

  • 概述

        按照libjingle文档关于Signal(https://developers.google.com/talk/libjingle/important_concepts#signals)的介绍,Signal机制实际上采用的是sigslot开源库(http://sourceforge.net/projects/sigslot/?source=directory)。sigslot是一个开源的回调框架,它可以使得类之间的回调使用的简单化,下面是libjingle文档中对sigslot的描述。

        sigslot is a generic framework that enables you to connect a calling member to a receiving function in any class (including the same class) very simply.

        Signal机制的工作方式参见下图的描述。源中设置一个或多个信号,目标为了在源的信号触发时获获得通知,需要连接到信号上。可以有多个目标发起连接,也可以同一个目标发起多个连接。连接创建好之后,源触发信号时,目标A和目标B就可以收到信号触发的消息了。

  • 使用

        libjingle中,源和目标都是对象,目标对象的类必须继承于sigslot::has_slots,信号是源对象的成员变量,必须是sigslot::signal?类型,其中?代表参数的个数(支持0到8),比如要设置带两个参数的信号,那么就是类型sigslot::signal2。要注意的是,libjingle中所有的信号变量为了方便,名称都加了前缀Signal。在目标对象初始化的时候,需要通过sigslot::signal?的connect函数将目标对象上的回调函数连接到信号上。当源对象触发信号时,目标函数的对应的回调函数会被一次调用。需要注意的是,回调函数的参数类型和个数需要和sigslot::signal?申明的一样。
        Signal机制的使用很简单,下面是libjingle文档中的例子。Sender代表的是源,Sender中的SignalDanger代表的是信号,是sigslot::signal2类型的变量。Receiver代表的是目标,继承自sigslot::has_slots。Receiver的构造函数中,通过调用SignalDanger的connect函数,连接函数OnDanger到SignalDanger上,当Sender调用Panic函数触发信号SignalDanger时,会转调到Receiver::OnDanger函数中。

// Class that sends the notification.
class Sender  {

  // The signal declaration. 
  // The '2' in the name indicates the number of parameters. Parameter types 
  // are declared in the template parameter list.
  sigslot::signal2<string message, std::time_t time> SignalDanger;

  // When anyone calls Panic(), we will send the SignalDanger signal.
  void Panic(){
    SignalDanger("Help!", std::time(0)); 
  }
 
 // Listening class. It must inherit sigslot.
class Receiver : public sigslot::has_slots<>{

  // Receiver registers to get SignalDanger signals.
  // When SignalDanger is sent, it is caught by OnDanger().
  // Second parameter gives address of the listener function class definition.
  // First parameter points to instance of this class to receive notifications.
  Receiver(Sender sender){ 
        sender->SignalDanger.connect(this, &Receiver.OnDanger);
  }

  // When anyone calls Panic(), Receiver::OnDanger gets the message.
  // Notice that the number and type of parameters match
  // those in Sender::SignalDanger, and that it doesn't return a value.
  void OnDanger(string message, std::time_t time){
    if(message == "Help!")
    { 
      // Call the police
      ...
    }
  }
...
}

  • 实现

        Signal机制的实现如下图所示。?代表的是0-8,sigslot::_connection类代表的是连接,其中包含了sigslot::has_slots对象和其中的回调函数地址。而sigslot::has_slots对象中则包含了所有连接的的sigslot::signal对象,这样的话可以在销毁时,断开和signal之间的连接。源码参见文件talk\base\sigslot.h。

        当调用signal的connect函数连接时,connect函数会创建_connection对象,然后将自己作为回调的发送者添加到has_slots中。

        类之间的继承关系如下图所示:

        SIGSLOT_DEFAULT_MT_POLICY是一个宏,定义如下。由于sigslot中的类,会对链表或集合进行操作,故SIGSLOT_DEFAULT_MT_POLICY只是增加了锁支持,若是单线程回调,则不需要锁。具体参见single_threaded和multi_threaded_local的实现。

#ifndef SIGSLOT_DEFAULT_MT_POLICY
#	ifdef _SIGSLOT_SINGLE_THREADED
#		define SIGSLOT_DEFAULT_MT_POLICY single_threaded
#	else
#		define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
#	endif
#endif

        has_slots类则维护了一个signal对象集合,支持has_slots对象复制操作,此时会将连接中的对像替换成新的对象。除此之外还支持连接和断开连接操作。其它的类,参见图中的描述。

  • 完整的Signal示例

        上面提到的Signal的用法,实际上是摘自libjingle文档。下面是基于此的一个完整的可运行的示例,此处只用到了sigslot库,sigslot库实际上就只有一个头文件sigslot.h。

#include <ctime>
#include <iostream>
#include <string>
#include "talk/base/sigslot.h"

class Sender  {
public:
  sigslot::signal2<std::string, std::time_t> SignalDanger;

  void Panic(){
    SignalDanger("Help!", std::time(0));
  }
};

class Receiver : public sigslot::has_slots<> {
public:
  Receiver(Sender& sender){
    sender.SignalDanger.connect(this, &Receiver::OnDanger);
  }

  void OnDanger(std::string message, std::time_t time){
    if(message == "Help!")
    {
      std::cout << "Call the police" << std::endl;
    }
  }
};

int main(int argc, char** argv)
{
  Sender sender;
  Receiver receiver(sender);
  sender.Panic();
  return 0;
}

抱歉!评论已关闭.