signals2 基於Boost裏的另外一個庫signals,實現了線程安全的觀察者模式。它是一種函數回調機制,當一個信號關聯了多個槽時,信號發出,這些槽將會被調用,固然,也能夠僅僅關聯一個槽函數。ios
其實Qt也提供了它本身的信號和槽機制,那個是很是的靈活和好用的,可是它依賴於Qt的框架,因此退而求其次,選擇了Boost提供了signals2;安全
signals2庫位於命名空間boost::signals2中,爲了使用它,須要包含頭文件<boost/signals2.hpp>;框架
信號(Signal)
signal是不可拷貝的,若是將signal做爲類的成員變量,那麼類將不能被拷貝,除非使用只能智能或者是引用間接的持有它;less
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),例如:spa
signal<void(int, double)> sig;
第二個模板參數Combiner是一個函數對象,它被稱爲‘合併器’,用於組合全部槽的返回值,默認是boost::signals2::optional_last_value<R>,返回最後一個被調用的槽的返回值;.net
第三個模板參數Group是槽編組的類型,你能夠爲你的槽設置不一樣的組,默認組的類型是int,一般狀況下,不須要更改;線程
鏈接(connect)
connection connect(const group_type &group,const slot_type &slot, connect_position position = at_back)
它做爲signal的成員函數,具備三個參數,第一個參數表示這個槽所屬的組,第二的參數表示信號觸發哪一個槽函數,而最後的參數,表示槽函數在響應隊列中響應的位置,默認at_back表示這個槽函數出來隊列的末尾,它將在其餘槽函數以後被調用。code
實例
不帶返回值的槽函數對象
#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; }
槽函數是類的成員函數的時候
#include <iostream> #include <boost/signals2.hpp> #include <boost/bind.hpp> #include <boost/function.hpp> using namespace std; using namespace boost; template <typename signature> class Signal{ public: //typedef 信號 typedef boost::signals2::signal<signature> defSignal; typedef typename defSignal::slot_type defSlot; public: //鏈接槽函數 boost::signals2::connection connectFun(const defSlot& slot); //重載僞函數 void operator()(typename defSignal::template arg<0>::type a0,typename defSignal::template arg<1>::type a1); private: defSignal mSignal; }; //接收信號後響應的函數 class FunRecv1{ public: void action(int a, int b){ cout << "add result" << a + b << endl; } }; //接收信號後響應的函數 class FunRecv2{ public: void action(int a, int b){ cout << "multi result" << a * b << endl; } }; //實現 template <typename signature> boost::signals2::connection Signal<signature>::connectFun(const defSlot& slot){ return mSignal.connect(slot); } template <typename signature> void Signal<signature>::operator()(typename defSignal::template arg<0>::type a0,typename defSignal::template arg<1>::type a1){ mSignal(a0,a1); } void main(){ Signal<void(int,int)> mysignal; FunRecv1 fun1; FunRecv2 fun2; //boost::function<void(int,int)> myfun = boost::bind(&FunRecv1::action,&fun1,_1,_2); //信號鏈接槽函數 boost::signals2::connection con1 = mysignal.connectFun(boost::bind(&FunRecv1::action,&fun1,100,200)); boost::signals2::connection con2 = mysignal.connectFun(boost::bind(&FunRecv2::action,&fun2,11,22)); mysignal(100,200); con2.disconnect(); mysignal(100,200); cin.get(); }
轉自:https://blog.csdn.net/qq_34347375/article/details/86620845