boost------signals2的使用1(Boost程序库完全开发指南)读书笔记
signals2基于Boost的另一個庫signals,實現了線程安全的觀察者模式。在signals2庫中,觀察者模式被稱為信號/插槽(signals and slots),他是一種函數回調機制,一個信號關聯了多個插槽,當信號發出時,所有關聯它的插槽都會被調用。
許多成熟的軟件系統都用到了這種信號/插槽機制(另一個常用的名稱是事件處理機制:event/event handler),它可以很好地解耦一組互相協作的類,有的語言設置直接內建了對它的支持(如c#),signals2以庫的形式為c++增加了這個重要的功能。
?
1、操作函數
signal最重要的操作函數是插槽管理connect()函數,它吧插槽連接到信號上,相當于為信號(事件)增加了一個處理的handler。
插槽可以是任意的可調用對象,包括函數指針、函數對象、以及它們的bind表達式和function對象,signal內部使用function作為容器來保存這些可調用對象。連接時可以指定組號也可以不指定組號,當信號發生時將依據組號的排序準則依次調用插槽函數。
如果連接成功connect()將返回一個connection,表示了信號與插槽之間的連接關系,它是一個輕量級的對象,可以處理兩者間的連接,如斷開、重連接、或者測試連接狀態。
成員函數disconnect()可以斷開插槽與信號的連接,它有兩種形式:傳遞組號將斷開該組的所有插槽,傳遞一個插槽對象將僅斷開該插槽。函數disconnect_all_slots()可以一次性斷開信號的所有插槽連接。
?
2、插槽的連接于使用
signal就像一個增強的function對象,它可以容納(使用connect())多個符合模板參數中函數簽名類型的函數(插槽),形成一個插槽鏈表,然后在信號發生時一起調用:
?
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "iostream" using namespace std;void slots1() {cout << "slots1 call" << endl; }void slots2() {cout << "slots2 call" << endl; }struct Hello {void operator()() const {std::cout << "Hello";} };int _tmain(int argc, _TCHAR* argv[]) {boost::signals2::signal<void()> sig;sig.connect(&slots1);sig.connect(&slots2);sig();boost::signals2::signal<void ()> sig1;sig1.connect(Hello());sig1();return 0; }?
?
注:編譯這個程序的時候,確保已經在stdafx.h中加入#define _SCL_SECURE_NO_WARNINGS或者,在C/C++----預處理器----預處理器定義中加上了_SCL_SECURE_NO_WARNINGS,否則會引發錯誤(或不能正確輸出),下同。
在連接插槽時省了了connect()的第二個參數connect_position,它的缺省值是at_back,表示插槽將插入到信號插槽鏈表的尾部,因此slot2將在slot1之后被調用。
下面是使用和組號的情況:
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "iostream" using namespace std;template<int N> struct Slot {void operator()(){cout << "Slot current N value is : " << N << endl;} };int _tmain(int argc, _TCHAR* argv[]) {boost::signals2::signal<void()> sig;sig.connect(Slot<1>(), boost::signals2::at_back); // 最后一個被調用sig.connect(Slot<99>(), boost::signals2::at_front); // 第一個被調用sig.connect(5, Slot<55>(), boost::signals2::at_back); // 組號5的最后一個sig.connect(5, Slot<57>(), boost::signals2::at_front);// 組號5的第一個sig.connect(10, Slot<100>());// 組號10該組只有一個sig();return 0; }?
3、信號的返回值
signal如function一樣,不僅可以把輸入參數轉發給所以插槽,也可以傳回插槽的返回值。默認情況下signal使用合并器optional_last_value<R>,它將使用optional對象返回最后被調用的插槽的返回值。
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "iostream" using namespace std;template<int N> struct Slot {int operator()(int x){cout << "Slot current N * x value is : " << endl;return (N * x);} };int _tmain(int argc, _TCHAR* argv[]) {boost::signals2::signal<int(int)> sig;sig.connect(Slot<10>());sig.connect(Slot<100>());cout << *sig(2) << endl;;return 0; }?
signal的operator()調用這時需要傳入一個整數參數,這個參數會被signal存儲一個拷貝,然后轉發給各個插槽。最后signal將返回插槽鏈表末尾slots<100>()的計算結果,它是一個optional對象,必須用接引用操作符*來獲得值(但你可以發現Slotcurrent N * x value is是輸出了兩次的)。
?
4、合并器
?
大多數時候,插槽的返回值都是有意義的,需要以某種方式處理多個插槽的返回值。
signal允許用戶自定義合并器來處理插槽的返回值,把多個插槽的返回值合并為一個結果返回給用戶。
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "numeric" #include "iostream" using namespace std;template<int N> struct Slot {int operator()(int x){cout << "Slot current N * x value is : " << endl;return (N * x);} };template<typename T> class combiner { public:typedef pair<T, T> result_type;combiner(T t = T()) : v(t){}template<typename InputIterator>result_type operator()(InputIterator begin, InputIterator end) const{if (begin == end){return result_type();}vector<T> vec(begin, end);T sum = accumulate(vec.begin(), vec.end(), v);T max = *max_element(vec.begin(), vec.end());return result_type(sum, max);}private:T v; };int _tmain(int argc, _TCHAR* argv[]) {boost::signals2::signal<int(int), combiner<int> > sig;sig.connect(Slot<10>());sig.connect(Slot<20>());sig.connect(Slot<3>());BOOST_AUTO(x, sig(2));cout << x.first << ", " << x.second << endl;return 0; }?
combiner類的調用操作符operator()的返回值類型可以是任意類型,完全由用戶指定,不一定必須是optional或者是插槽的返回值類型。operator()的模板參數InputIterator是插槽鏈表的返回值迭代器,可以使用它來遍歷所有插槽的返回值,進行所需的處理。
當信號被調用時,signal會自動把引用操作轉換為插槽調用,將調用給定的合并器的operator()逐個處理插槽的返回值,并最終返回合并器operator()的結果。
如果我們不適用signal的缺省構造函數,而是在構造signal時傳入一個合并器的實例,那么signal將使用逐個合并器(的拷貝)處理返回值。例如,下面的代碼使用了一個有初值的合并器對象,累加值從100開始:
signal<int(int),combiner<int> > sig(combiner<int>(100));
?
?
5、管理信號的連接
?
信號與插槽的鏈接并不要求是永久的,當信號調用玩插槽后,有可能需要把插槽從信號中斷開,再連接到其他的信號上去。
signal可以用成員函數disconnect()斷開一個或一組插槽,或者使用disconnect_all_slots()斷開所有插槽連接,函數empty()和num_slots()用來檢測信號當前插槽的連接狀態。
要斷開一個插槽,插槽必須能夠進行登記等價比較,對于函數對象來說就是重載一個等價語義的operator==。下面對slots<N>增加一個等價比較:
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "numeric" #include "iostream" using namespace std;template<int N> struct Slot {int operator()(int x){cout << "Slot current N * x value is : " << endl;return (N * x);} };template<int N> bool operator== (const Slot<N>& a, const Slot<N>& b) {return true; }template<typename T> class combiner { public:typedef pair<T, T> result_type;combiner(T t = T()) : v(t){}template<typename InputIterator>result_type operator()(InputIterator begin, InputIterator end) const{if (begin == end){return result_type();}vector<T> vec(begin, end);T sum = accumulate(vec.begin(), vec.end(), v);T max = *max_element(vec.begin(), vec.end());return result_type(sum, max);}private:T v; };int _tmain(int argc, _TCHAR* argv[]) {boost::signals2::signal<int(int)> sig;// assert(sig.empty());sig.connect(0, Slot<10>());sig.connect(Slot<20>());sig.connect(1, Slot<30>());assert(sig.num_slots() == 3);sig.disconnect(0);// assert(sig.num_slots() == 1);sig.disconnect(Slot<30>());// assert(sig.empty());sig(2);return 0; }?
6、更靈活的管理信號連接
signals2庫提供另外一種較為靈活的連接管理方式:使用connection對象。
每當signal使用connect()連接插槽時,他就會返回一個connection對象。connection對象像是信號與插槽連接關系的一個句柄(handle),可以管理鏈接:
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "numeric" #include "iostream" using namespace std;template<int N> struct Slot {int operator()(int x){cout << "Slot current N * x value is : " << endl;return (N * x);} };template<int N> bool operator== (const Slot<N>& a, const Slot<N>& b) {return true; }template<typename T> class combiner { public:typedef pair<T, T> result_type;combiner(T t = T()) : v(t){}template<typename InputIterator>result_type operator()(InputIterator begin, InputIterator end) const{if (begin == end){return result_type();}vector<T> vec(begin, end);T sum = accumulate(vec.begin(), vec.end(), v);T max = *max_element(vec.begin(), vec.end());return result_type(sum, max);}private:T v; };int _tmain(int argc, _TCHAR* argv[]) {boost::signals2::signal<int(int)> sig;boost::signals2::connection c1 = sig.connect(0, Slot<10>());boost::signals2::connection c2 = sig.connect(0, Slot<10>());boost::signals2::connection c3 = sig.connect(0, Slot<10>());c1.disconnect();assert(sig.num_slots() == 2);assert(!c1.connected());assert(c2.connected());return 0; }另外一種連接管理對象是scoped_connection,它是connection的種類,提供類似scoped_ptr的RAII功能:插槽與信號的連接僅在作用域內生效,當離開作用域時連接就會自動斷開。當需要臨時連接信號時scoped_connection會非常有用:
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "numeric" #include "iostream" using namespace std;template<int N> struct Slot {int operator()(int x){cout << "Slot current N * x value is : " << endl;return (N * x);} };template<int N> bool operator== (const Slot<N>& a, const Slot<N>& b) {return true; }template<typename T> class combiner { public:typedef pair<T, T> result_type;combiner(T t = T()) : v(t){}template<typename InputIterator>result_type operator()(InputIterator begin, InputIterator end) const{if (begin == end){return result_type();}vector<T> vec(begin, end);T sum = accumulate(vec.begin(), vec.end(), v);T max = *max_element(vec.begin(), vec.end());return result_type(sum, max);}private:T v; };int _tmain(int argc, _TCHAR* argv[]) {boost::signals2::signal<int(int)> sig;sig.connect(0, Slot<10>());assert(sig.num_slots() == 1);{boost::signals2::scoped_connection sc = sig.connect(0, Slot<20>());assert(sig.num_slots() == 2);}assert(sig.num_slots() == 1);return 0; }?
插槽與信號的連接一旦斷開就不能再連接起來,connection不提供reconnet()這樣的函數。但可以暫時地阻塞插槽與信號的連接,當信號發生時被阻塞的插槽將不會被調用,connection對象的blocked()函數可以檢查插槽是否被阻塞。但被阻塞的插槽并沒有斷開與信號的鏈接,在需要的時候可以隨時解除阻塞。
connection對象自身沒有阻塞的功能,他需要一個輔助類shared_connection_block,它將阻塞connection對象,知道它被析構或者顯式調用unblock()函數:
?
#include "stdafx.h" #include "boost/utility/result_of.hpp" #include "boost/typeof/typeof.hpp" #include "boost/assign.hpp" #include "boost/ref.hpp" #include "boost/bind.hpp" #include "boost/function.hpp" #include "boost/signals2.hpp" #include "numeric" #include "iostream" using namespace std;template<int N> struct Slot {void operator()(int x){cout << "Slot current N is : " << N << endl;} };template<int N> bool operator== (const Slot<N>& a, const Slot<N>& b) {return true; }template<typename T> class combiner { public:typedef pair<T, T> result_type;combiner(T t = T()) : v(t){}template<typename InputIterator>result_type operator()(InputIterator begin, InputIterator end) const{if (begin == end){return result_type();}vector<T> vec(begin, end);T sum = accumulate(vec.begin(), vec.end(), v);T max = *max_element(vec.begin(), vec.end());return result_type(sum, max);}private:T v; };int _tmain(int argc, _TCHAR* argv[]) {boost::signals2::signal<void(int)> sig;boost::signals2::connection c1 = sig.connect(0, Slot<10>());boost::signals2::connection c2 = sig.connect(0, Slot<20>());assert(sig.num_slots() == 2);sig(2);cout << "begin blocking..." << endl;{boost::signals2::shared_connection_block block(c1);assert(sig.num_slots() == 2);assert(c1.blocked());sig(2);}cout << "end blocking.." << endl;assert(!c1.blocked());sig(2);return 0; }?
轉載于:https://www.cnblogs.com/jiangu66/p/3230969.html
總結
以上是生活随笔為你收集整理的boost------signals2的使用1(Boost程序库完全开发指南)读书笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET下安装卸载WindowsServ
- 下一篇: 新站收录记录