libjingle源碼分析之一:Signal機制

  • 摘要

        本文主要分析了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;
}


發佈了86 篇原創文章 · 獲贊 10 · 訪問量 43萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章