Gtalk源码剖析之:sigslot介绍

Posted ouyangbuxiu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gtalk源码剖析之:sigslot介绍相关的知识,希望对你有一定的参考价值。

Sigslot 介绍,基于sigslot作者的介绍文档,讲述sigslot由来原理。 简介: sigslot是一个线程安全、类型安全,用C++实现的sig/slot机制(sig/slot机制就是对象之间发送和接收消息的机制)的开源代码库。作者: Sarah Thompson sigslot sourceforget 上的连接为 http://sigslot.sourceforge.net/ ,可以到那里获取关于 sigslot 的最新消息。   sigslot起源 现代的C++项目通常包含大量的C++类和对象,对象之间通过成员函数调用,缺点是当类和对象规模很大时,相互之间必须记住对方提供了哪些接口,以及接口的详细信息,不易维护。 比如:我们有一个switch类和一个light类,而我们现在需要将两者关联起来,即通过switch控制light的状态,我们可能需要添加一个另外的类ToggleSwitch来将两者关联起来: class Switch public :      virtual void Clicked() = 0; ; class Light public :      void ToggleState();      void TurnOn();      void TurnOff(); ;   class ToggleSwitch : public Switch public :      ToggleSwitch(Light& lp)               m_lp = lp;           virtual void Clicked()               m_lp.ToggleState();      private :      Light& m_lp; ; Light lp1, lp2; ToggleSwitch tsw1(lp1), tsw2(lp2); 这在功能上完全可以实现,但想象一下如果大量的需要相互交互消息的类,用这种方式维护起来会是什么场景?   使用sig/slot机制来解决上述情况,不需要关心关联类的接口细节,sigslot实现的switch和light上述功能如下: class Switch public :      signal0<> Clicked; ; class Light : public has_slots<> public :      void ToggleState();      void TurnOn();      void TurnOff(); ; Switch sw1, sw2; Light lp1, lp2; Sigslot机制实现该功能与第一种方法相比,switch类多了个signal成员,light类需要从has_slots<>继承,其他没有什么变化。但省去了编写继承类用来实现两者关联的ToggleSwitch,对于sigslot机制我们只需要   sw1.Clicked.connect(&lp1, &Light::ToggleState); sw2.Clicked.connect(&lp2, &Light::ToggleState);   就可以让两者之间建立关系,并且不需要修改类的代码就可以使用将来的变化,比如,如果想添加两个额外的灯和开关;或者增加一个按钮控制所有的灯,只需要 Switch sw3, sw4, all_on, all_off; Light lp3, lp4; sw3.Clicked.connect(&lp3, &Light::ToggleState); sw4.Clicked.connect(&lp4, &Light::ToggleState); all_on.Clicked.connect(&lp1, &Light::TurnOn()); all_on.Clicked.connect(&lp2, &Light::TurnOn()); all_on.Clicked.connect(&lp3, &Light::TurnOn()); all_on.Clicked.connect(&lp4, &Light::TurnOn()); all_off.Clicked.connect(&lp1, &Light::TurnOff()); all_off.Clicked.connect(&lp2, &Light::TurnOff()); all_off.Clicked.connect(&lp3, &Light::TurnOff()); all_off.Clicked.connect(&lp4, &Light::TurnOff()); 很简洁,很优雅,不是吗?是不是还没明白,还有些糊涂?没关系,让我们继续向下,很快就会柳暗花明!     参数类型 sig/slot,什么是sig,什么是slot? 让我们明确一下,上例中继承has_slots<>的类light的成员函数void ToggleState();我们称之为slot;而switch类的signal0<> Clicked;称之为sig;sigslot的核心就在这里,就是通过这两个建立对应关系来实现对象间的消息交互。 我们可以看出slot是一个成员函数,而sig是一个成员变量,这里所说的slot参数类型是指对应的成员函数的参数类型,需要注意的是slot的原形需要与sig一致。嗯??读者可能奇怪一个成员函数如何与一个变量类型一致?从何说起?呵呵,慢慢来,sigslot库的实现使用了signaln <type1, type2, ...>这种书写方式,其中n表示signal可以接收几个参数,上面的列子中switch类的signal0<> Clicked;我们知道Clicked这个sig参数个数为n=0,而light的成员函数void ToggleState();参数个数也是0,这是巧合吗?不是,sigslot库正是以这种机制实现的。读者可能想起,函数原型除了函数参数以外还有返回值类型阿,没错,sigslot库的作者认为没必要支持各种类型返回值的slot,限制了只支持返回值为void类型的slot,OK,具体细节我们到sigslot源代码中再看。   Sigslot库用法 发送信号 信号(sig,即sig/slot的sig,下面提到的信号等同于此含义): signal1<char *, int> ReportError; 比如上面的一个ReportError这个信号,当调用ReportError("Something went wrong", ERR_SOMETHING_WRONG);时候,将自动调用ReportError的emit成员函数发出一个信号。发给谁呢?^_^ 连接信息号 通过调用sig的connect函数建立sig和slot间的对应关系。Connect函数接收两个参数,一个是消息目的对象的地址(指针),另一个是目的对象的成员函数指针(slot)。为了让整个机制有效运行,目的类必须从has_slots<>继承,并且sig/slot参数类型必须一致。 也可以将一个sig连接到多个slot上,这样每次sig发出信号的时候,每个连接的slot都能收到该信号。 断开信号连接 通过调用sig的disconnect函数断开sig和slot之间的连接,只有一个参数:目的对象的地址。一般不需要显式调用disconnect函数,在sig类和目的类(包含slot函数的类)析构函数中将自动调用disconnect断开sig和slot的连接。   也可使用disconnect_all断开该sig的所有slot。 signal0<> Bang(); Bang.connect(&bomb, &Bomb::Explode); Bang.connect(&bomb2, &Bomb::Explode); Bang.connect(&secret_base, &SecretBase::SelfDestruct); Bang.disconnect_all(); Bang(); // Safely defused!   slot 如上所述slot就是普通的成员函数,但有以下限制: 1、返回值必须为void 2、Slot参数个数范围为0-8个 3、实现slot的类必须继承自has_slots<> 1,2是sigslot库作者的限制,作者权衡各方面因素后做出的决定,如果你觉得有必要你可以修改sigslot代码取消该限制,而3是sigslot的机制基础,必须遵守,除非你自己重新写个sigslot   需要注意的是:sigslot库的设计,当发送一个没有连接的信号时,不做任何处理,也不会有错误发出。

以上是关于Gtalk源码剖析之:sigslot介绍的主要内容,如果未能解决你的问题,请参考以下文章

Gtalk源码剖析之:sigslot介绍

Gtalk源码剖析之:sigslot源代码

Gtalk源码剖析之:sigslot源代码

Gtalk源码剖析之:sigslot源代码

gRPC-go源码剖析五十三之取消功能相关介绍以及测试用例介绍

STL源码剖析之allocator