signals2 基于Boost里的另一个库signals,实现了线程安全的观察者模式。它是一种函数回调机制,当一个信号关联了多个槽时,信号发出,这些槽将会被调用。google的base库里用的多的模式是:observer,delegate,callback。用来做相互调用的解耦,但是感觉也没有信号槽用的方便。
其实Qt也提供了它自己的信号和槽机制,那个是非常的灵活和好用的,但是它依赖于Qt的框架,所以退而求其次,选择了Boost提供了signals2
;
signals2库位于命名空间boost::signals2
中,为了使用它,需要包含头文件
;
文章目录
信号(Signal)
连接(connect)
实例
不带返回值的槽函数
合并器
断开连接
临时连接
阻塞连接
触发成员中的槽函数
自动断开
signal是不可拷贝的,如果将signal作为类的成员变量,那么类将不能被拷贝,除非使用只能智能或者是引用间接的持有它;
signal是一个模板类,它的定义如下:
template,
typename Group = int, typename GroupCompare = std::less,
typename SlotFunction = boost::function,
typename ExtendedSlotFunction = boost::function,
typename Mutex = boost::signals2::mutex>
class signal;
第一个模板参数Signature
的含义和function
相同,也是一个函数类型,表示signal调用的函数(槽,事件处理handler),例如:
signal sig;
第二个模板参数Combiner
是一个函数对象,它被称为‘合并器’,用于组合所有槽的返回值,默认是boost::signals2::optional_last_value
,返回最后一个被调用的槽的返回值;
第三个模板参数Group
是槽编组的类型,你可以为你的槽设置不同的组,默认组的类型是int,通常情况下,不需要更改;
connection connect(const group_type &group,const slot_type &slot, connect_position position = at_back)
它作为signal的成员函数,具有三个参数,第一个参数表示这个槽所属的组,第二的参数表示信号触发哪个槽函数,而最后的参数,表示槽函数在响应队列中响应的位置,默认at_back
表示这个槽函数出来队列的末尾,它将在其他槽函数之后被调用。
#include
#include
using namespace boost::signals2;
void slots1() {
std::cout << "slot 1 called" << std::endl;
}
void slots2(int a) {
std::cout << "slot 2 called " << a << std::endl;
}
void slots3(int a) {
std::cout << "slot 3 called " << a << std::endl;
}
void slots4(int a) {
std::cout << "slot 4 called " << a << std::endl;
}
int main() {
signalsig1;
sig1.connect(&slots1);
sig1(); // the slot 1 called
signalsig2;
sig2.connect(1, &slots2);
sig2.connect(2, &slots3);
sig2.connect(2, &slots4, at_front); // slot 4 处于 第二组的最前面
// 槽函数的调用,首先是比较连接的组的先后循序,然后根据组内循序调用;
sig2(2); // slot 2 called slot 4 called slots3 called
return 0;
}
当槽函数带参数的时候,参数是通过信号传递的,所以需要保持信号和槽的参数的个数一致
结果如下:
带参数的槽函数
#include
#include
using namespace boost::signals2;
int slots1(int a) {
std::cout << "slot 1 called " << a << std::endl;
return a + 1;
}
int slots2(int a) {
std::cout << "slot 2 called " << a << std::endl;
return a + 2;
}
int slots3(int a) {
std::cout << "slot 3 called " << a << std::endl;
return a + 3;
}
int main() {
signal sig;
sig.connect(&slots1);
sig.connect(&slots2, at_front);
sig.connect(&slots3);
std::cout << *sig(0) << std::endl;
return 0;
}
在默认情况下,一个信号连接多个槽函数,并且槽函数是带有返回值的,那么这个信号将返回槽函数队列中的最后一个的返回值。
结果如下:
自定义合并器可以让我们处理多个槽的返回值;
template
struct Combiner {
typedef vector result_type;
template
result_type operator()(InputIterator first, InputIterator last) const {
if(first == last) {
return result_type(0);
}
return result_type(first, last);
}
};
这是一个典型的合并器,它返回一个拥有所有槽的返回值的一个vector,我们可以随便定义合并器的返回类型,但要注意,一定要通过 typedef your_type result_type
去注册一下你的返回值类型;
具体的用法如下:
#include "boost/signals2.hpp"
#include
#include
using namespace std;
using namespace boost::signals2;
template
struct Combiner {
typedef vector result_type;
template
result_type operator()(InputIterator first, InputIterator last) const {
if(first == last) {
return result_type(0);
}
return result_type(first, last);
}
};
int slots3(int x) {
return x + 3;
}
int slots4(int x) {
return x + 4;
}
int main() {
signal > sig;
sig.connect(&slots3);
sig.connect(&slots4);
auto result = sig(1);
for(const auto& i : result) {
cout << i << endl;
}
return 0;
}
// 以上省略一些代码
sig.connect(0, &slots1);
sig.connect(0, &slots2);
connection c1 = sig.connect(1, &slots3);
sig.connect(2, &slots4);
sig.connect(2, &slots5);
sig();
sig.disconnect(0); // 断开组号为0的连接
cout << sig.num_slots() << endl; // 还有三个连接
sig();
sig.disconnect(2); // 断开组号为2的连接
sig();
c1.disconnect(); // 断开slot3的连接
以上两种方法都是可以的;
Boost提供了一个临时的连接方式scoped_connection
,也就是有作用域的连接;
// 以上省略了一些代码
sig.connect(&slots1);
{ // 进入作用域, 建立临时的连接
scoped_connection sc = sig.connect(&slots2);
cout << sig.num_slots() << endl;
} // 离开作用域就自动断开了连接
cout << sig.num_slots() << endl;
Boost提供了一个shared_connection_block
实现阻塞和解除阻塞连接的操作,当它被析构(离开作用域)或者被显式的调用unblock()
就好解除阻塞;
// 以上省略一些代码
connection c1 = sig.connect(slots1);
connection c2 = sig.connect(slots2);
connection c3 = sig.connect(slots3);
connection c4 = sig.connect(slots4);
sig();
{
shared_connection_block block(c1); // 阻塞了c1
sig(); //c1不会被调用
}
sig();
我们使用signal通常是为了实现类间的通信,实现观察者模式;
我们需要使用bind()函数绑定槽函数,返回函数对象;
#include "boost/signals2.hpp"
#include
#include
using namespace std;
using namespace boost::signals2;
class C_Slots1 {
public:
int SL(int a) {
cout << "slot 1 called" << a << endl;
return a;
}
void SL1(int a, int b) {
cout << "slot 2 called " << a << " " << b << endl;
}
};
int main() {
signal sig1;
sig1.connect(bind(&C_Slots1::SL, &cs_1,_1)); // 绑定对象的成员
signalsig2;
sig2.connect(bind(&C_Slots1::SL1,&cs_1, _1, _2));
cout << *sig1(10) << endl;
sig2(1, 2);
return 0;
}
当槽函数被意外销毁时,信号调用会发生未定义的行为。我们希望它能够跟踪槽函数的生命周期,当槽函数失效时,连接会自动断开;
我们通过boost::shared_ptr来管理槽函数的生命周期,track()函数来跟踪槽所使用的资源;(boost::shared_ptr与std::shared_ptr功能上一样,但是实现不一样,是不一样的!!!)
#include "boost/signals2.hpp"
#include
#include
using namespace std;
using namespace boost::signals2;
class C_Slots {
public:
int SL(int a) const{
cout << "slot 1 called" << a << endl;
return a;
}
};
int main() {
typedef signal signal_t;
signal_t sig;
boost::shared_ptr p_c1(new C_Slots2());
sig5.connect(signal_t::slot_type(&C_Slots::SL, p_c1.get(), _1).track(p_c1));
cout << *sig(2) << endl;
return 0;
}
1、signals2实现了线程安全的“观察者模式”,也称作:信号---插槽,他是一种函数的回调机制,当信号发出时,相应的槽函数会被调用,有点类似于QT中的信号槽。
2、特点:
(1)、一个信号可以与多个插槽函数绑定;
(2)、一个信号与多个插槽函数绑定时,插槽函数可以设置自己被调用的顺序:boost::signals2::at_front、boost::signals2::at_back;
(3)、一个信号与多个插槽函数绑定时,可以对插槽函数进行分组:组号小的函数先调用,组号大的后调用;同一组号中,调用顺序根据boost::signals2::at_back等设置的顺序调用;
(4)、信号与插槽函数一旦断开连接,就不能再次被绑定;
(5)、当前信号绑定的插槽函数数目:num_slots() ,信号是否绑定了插槽函数:empty();
(6)、我们可以通过合并器获取插槽函数被调用后的返回值;
(7)、以下类可以帮助我们进行更加灵活的信号连接管理,boost::signals2::connection:断开连接、判断是否连接;boost::signals2::shared_connection_block:可以阻塞连接、解除阻塞连接、判断当前连接是否阻塞;boost::signals2::scoped_connection:对象析构时会自动释放连接;
// Single_test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
#include
#include
using namespace std;
void func1()
{
std::cout << "func1()函数被调用." << std::endl;
}
void func2()
{
std::cout << "func2()函数被调用." << std::endl;
}
void func3()
{
std::cout << "func3()函数被调用." << std::endl;
}
class my_object
{
public:
void operator()(int param)
{
std::cout << "仿函数被调用: " << param << std::endl;
}
};
//测试组号时用
template
struct Slot
{
void operator()()
{
std::cout << "Slot current N value is : " << N << std::endl;
}
};
int func4(int param)
{
std::cout << "func3()函数被调用." << std::endl;
return param + 10;
}
//合并器
template
struct Combiner {
typedef vector result_type;
template
result_type operator()(InputIterator first, InputIterator last) const {
if (first == last) {
return result_type(0);
}
return result_type(first, last);
}
};
int func5(int param)
{
std::cout << "func5()函数被调用." << std::endl;
return param + 5;
}
int func6(int param)
{
std::cout << "func6()函数被调用." << std::endl;
return param + 6;
}
int func7(int param)
{
std::cout << "func7()函数被调用." << std::endl;
return param + 7;
}
int func8(int param)
{
std::cout << "func8()函数被调用." << std::endl;
return param + 8;
}
int main()
{
//设置槽的调用顺序
boost::signals2::signal sig_void;
sig_void.connect(&func1);
sig_void.connect(&func2, boost::signals2::at_front);//将会第一个被调用
sig_void();
//设置槽带参数
boost::signals2::signal sig_obj;
sig_obj.connect(my_object());
sig_obj(10);
//设置组号:根据组号分成组操作,组号小的函数先调用,组号大的后调用;
//同一组号,调用顺序根据boost::signals2::at_back等设置的顺序调用
boost::signals2::signal sig_slot;
sig_slot.connect(10, Slot<1>());
sig_slot.connect(10, Slot<2>());
sig_slot.connect(9, Slot<3>());
sig_slot.connect(9, Slot<4>(), boost::signals2::at_back);
sig_slot.connect(11, Slot<5>());
sig_slot.connect(11, Slot<6>());
sig_slot.connect(11, Slot<7>(), boost::signals2::at_front);
std::cout << "sig_slot信号关联的插槽数量: " << sig_slot.num_slots() << std::endl;
if(!sig_slot.empty())
sig_slot();
//接收槽函数的返回值
boost::signals2::signal sig_return;
sig_return.connect(&func4);
//一个信号对应一个槽函数时可以
int m = *sig_return(100);
std::cout << m << std::endl;
//断开之前所有的信号-槽连接
sig_return.disconnect_all_slots();
//一个信号对应多个槽函数时:使用合并器获取所有的返回值
boost::signals2::signal > sig_ret_combin;
sig_ret_combin.connect(&func5);
sig_ret_combin.connect(&func6);
auto result = sig_ret_combin(200);
for (const auto& i : result)
{
std::cout << i << std::endl;
}
//通过boost::signals::shared_connection_block 管理连接
boost::signals2::signal sig_mgr;
boost::signals2::shared_connection_block c = sig_mgr.connect(&func7);
c.block();//连接被阻塞
if (c.blocking())
std::cout << "连接被阻塞" << std::endl;
sig_mgr(12);//不会被调用
c.unblock();//解除阻塞
sig_mgr(12);//会被调用
//boost::signals::connection管理连接
boost::signals2::signal sig_mgr1;
boost::signals2::connection c1 = sig_mgr1.connect(&func7);
if (c1.connected())
sig_mgr1(12);
//断开连接
c1.disconnect();
//boost::signals::scoped_connection管理:析构时会自动释放连接
boost::signals2::signal sig_mgr2;
{
boost::signals2::scoped_connection c2 = sig_mgr2.connect(&func8);
std::cout << "当前fun8的连接数: " << sig_mgr2.num_slots() << std::endl;
}
//连接已被释放,相应槽函数不会被调用
sig_mgr2(12);
return 1;
}
转载自:https://blog.csdn.net/qq_34347375/article/details/86620845