本文主要分析了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.
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对象复制操作,此时会将连接中的对像替换成新的对象。除此之外还支持连接和断开连接操作。其它的类,参见图中的描述。
#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; }