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