signals2 基於Boost裏的另一個庫signals,實現了線程安全的觀察者模式。它是一種函數回調機制,當一個信號關聯了多個槽時,信號發出,這些槽將會被調用。google的base庫裏用的多的模式是:observer,delegate,callback。用來做相互調用的解耦,但是感覺也沒有信號槽用的方便。
其實Qt也提供了它自己的信號和槽機制,那個是非常的靈活和好用的,但是它依賴於Qt的框架,所以退而求其次,選擇了Boost提供了signals2
;
signals2庫位於命名空間boost::signals2
中,爲了使用它,需要包含頭文件<boost/signals2.hpp>
;
信號(Signal)
signal是不可拷貝的,如果將signal作爲類的成員變量,那麼類將不能被拷貝,除非使用只能智能或者是引用間接的持有它;
signal是一個模板類,它的定義如下:
template<typename Signature,
typename Combiner = boost::signals2::optional_last_value<R>,
typename Group = int, typename GroupCompare = std::less<Group>,
typename SlotFunction = boost::function<Signature>,
typename ExtendedSlotFunction = boost::function<R (const connection &, T1, T2, ..., TN)>,
typename Mutex = boost::signals2::mutex>
class signal;
第一個模板參數Signature
的含義和function
相同,也是一個函數類型,表示signal調用的函數(槽,事件處理handler),例如:
signal<void(int, double)> sig;
第二個模板參數Combiner
是一個函數對象,它被稱爲‘合併器’,用於組合所有槽的返回值,默認是boost::signals2::optional_last_value<R>
,返回最後一個被調用的槽的返回值;
第三個模板參數Group
是槽編組的類型,你可以爲你的槽設置不同的組,默認組的類型是int,通常情況下,不需要更改;
連接(connect)
connection connect(const group_type &group,const slot_type &slot, connect_position position = at_back)
它作爲signal的成員函數,具有三個參數,第一個參數表示這個槽所屬的組,第二的參數表示信號觸發哪個槽函數,而最後的參數,表示槽函數在響應隊列中響應的位置,默認at_back
表示這個槽函數出來隊列的末尾,它將在其他槽函數之後被調用。
實例
不帶返回值的槽函數
#include <iostream>
#include <boost/signals2.hpp>
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() {
signal<void()>sig1;
sig1.connect(&slots1);
sig1(); // the slot 1 called
signal<void(int)>sig2;
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 <iostream>
#include <boost/signals2.hpp>
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<int(int)> sig;
sig.connect(&slots1);
sig.connect(&slots2, at_front);
sig.connect(&slots3);
std::cout << *sig(0) << std::endl;
return 0;
}
在默認情況下,一個信號連接多個槽函數,並且槽函數是帶有返回值的,那麼這個信號將返回槽函數隊列中的最後一個的返回值。
結果如下:
合併器
自定義合併器可以讓我們處理多個槽的返回值;
template<typename T>
struct Combiner {
typedef vector<T> result_type;
template<typename InputIterator>
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 <iostream>
#include <vector>
using namespace std;
using namespace boost::signals2;
template<typename T>
struct Combiner {
typedef vector<T> result_type;
template<typename InputIterator>
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<int(int), Combiner<int> > 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 <iostream>
#include <vector>
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<int(int)> sig1;
sig1.connect(bind(&C_Slots1::SL, &cs_1,_1)); // 綁定對象的成員
signal<void(int, int)>sig2;
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 <iostream>
#include <vector>
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<int(int)> signal_t;
signal_t sig;
boost::shared_ptr<C_Slots> 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;
}
boost---signals2使用詳解
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 <iostream>
#include <vector>
#include <boost/signals2.hpp>
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<int N>
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<typename T>
struct Combiner {
typedef vector<T> result_type;
template<typename InputIterator>
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<void()> sig_void;
sig_void.connect(&func1);
sig_void.connect(&func2, boost::signals2::at_front);//將會第一個被調用
sig_void();
//設置槽帶參數
boost::signals2::signal<void(int)> sig_obj;
sig_obj.connect(my_object());
sig_obj(10);
//設置組號:根據組號分成組操作,組號小的函數先調用,組號大的後調用;
//同一組號,調用順序根據boost::signals2::at_back等設置的順序調用
boost::signals2::signal<void()> 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<int(int)> sig_return;
sig_return.connect(&func4);
//一個信號對應一個槽函數時可以
int m = *sig_return(100);
std::cout << m << std::endl;
//斷開之前所有的信號-槽連接
sig_return.disconnect_all_slots();
//一個信號對應多個槽函數時:使用合併器獲取所有的返回值
boost::signals2::signal<int(int), Combiner<int> > 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<int(int)> 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<int(int)> 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<int(int)> 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