C++ Boost signal2信號/槽函數


signals2 基於Boost里的另一個庫signals,實現了線程安全的觀察者模式。它是一種函數回調機制,當一個信號關聯了多個槽時,信號發出,這些槽將會被調用,當然,也可以僅僅關聯一個槽函數。

其實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;
}

 

槽函數是類的成員函數的時候

#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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM