2012年12月7日 星期五

sig/slot

大學沒好好的學.... 只好有空看看對岸的牛人...orz


http://blog.csdn.net/smallcraft/article/details/2237802


 

Sigslot介紹

分類: C/C++學習2812人閱讀 評論 (3) 收藏 舉報
  最近在開發一個基於libjingle開源的IM系統,裡面有一個其類為has_slots,搜索了一下其資料發現是一個很好用的C++庫,先對其簡單介紹一下。
1.          簡介
      sigslot是一個線程安全、類型安全,用C++實現的sig/slot機制(sig/slot機制就是對象之間發送和接收消息的機制)的開源代碼庫。是一個非常好用的庫,只有一個頭文件sigslot.h。
2.    Sigslot實例
      現代的C++項目通常包含大量的C++類和對象,對象之間通過成員函數調用,缺點是當類和對象規模很大時,相互之間必須記住對方提供了哪些接口,以及接口的詳細信息,很不方便。
比如:我們有一個switch類和一個light類,而我們現在需要將兩者關聯起來,即通過switch控制light的狀態,我們可能需要添加一個另外的類ToggleSwitch來將兩者關聯起來:
class Switch
{
public:
     virtual void Clicked() = 0;
};
class Light
{
public:
     void ToggleState();
     void TurnOn();
     void TurnOff();
};
class ToggleSwitch : public Switch
{
public:
     ToggleSwitch(Light& lp){m_lp = lp;}
     virtual void Clicked(){m_lp.ToggleState();}
private:
     Light& m_lp;
};
Light lp1, lp2;
ToggleSwitch tsw1(lp1), tsw2(lp2);
這在功能上完全可以實現,但想像一下如果大量的需要相互交互消息的類,那工作量就不是一般的大了。 
使用sig/slot機制來解決上述情況,不需要關心關聯類的接口細節,sigslot實現的switch和light上述功能如下:
class Switch
{
public:
     signal0<> Clicked;
};
class Light : public has_​​slots<>
{
public:
     void ToggleState();
     void TurnOn();
     void TurnOff();
};
Switch sw1, sw2;
Light lp1, lp2;
   
     Sigslot機制實現該功能與第一種方法相比,switch類多了個signal0成員,light類需要從has_slots<>繼承,其他沒有什麼變化,但省去了編寫繼承類用來實現兩者關聯的ToggleSwitch 。
下面是實現功能的簡單代碼。
 
#include <iostream>
using namespace std;
 
#include "sigslot.h"
using namespace sigslot; //必須加上sigslot的命名空間
//在用vs調試時還需要將sigslot.h中很多的自定義模板結構類型前加typename
const int TRUE = 1;
const int FALSE = 0;
class Switch
{
public:
       signal0<> Clicked;
//這裡的信號是不帶參數的,signaln表示帶幾個參數
};
class Light : public has_​​slots<>
{
public:
       Light(bool state){b_state = state;Displaystate();}
       void ToggleState(){b_state = !b_state;Displaystate();} //作為消息的響應
       void TurnOn(){b_state = TRUE;Displaystate();}
       void TurnOff(){b_state = FALSE;Displaystate();}
       void Displaystate(){cout<<"The state is "<<b_state<<endl;}
private:
       bool b_state;
};
void main()
{
       Switch sw1, sw2,all_on,all_off;
       Light lp1(TRUE), lp2(FALSE);
       sw1.Clicked.connect(&lp1,&Light::ToggleState); //綁定
       sw2.Clicked.connect(&lp2,&Light::ToggleState);
       all_on.Clicked.connect(&lp1,&Light::TurnOn);
       all_on.Clicked.connect(&lp2,&Light::TurnOn);
       all_off.Clicked.connect(&lp1,&Light::TurnOff);
       all_off.Clicked.connect(&lp2,&Light::TurnOff);
 
       sw1.Clicked();
       sw2.Clicked();
       all_on.Clicked();
       all_off.Clicked();
 
       sw1.Clicked.disconnect(&lp1);
       sw2.Clicked.disconnect(&lp2);
       all_on.Clicked.disconnect(&lp1);
       all_on.Clicked.disconnect(&lp2);
       all_off.Clicked.disconnect(&lp1);
       all_off.Clicked.disconnect(&lp2);
}
 
 
  。  參數類型
        sig/slot可以帶參數也可以不帶,最多可以帶8個參數。重新回顧上例,switch類的signal0<> Clicked,稱之為sig,即用來發出信號;而繼承has_slots<>的類light的成員函數void ToggleState() Turnon() Turnoff(),稱之為slot ,即信號的處理函數。sigslot的核心就在這裡,就是通過這兩個建立對應關係來實現對象間的消息交互。
sig是一個成員變量,它形如
 
signal+n<type1,type2……>
    後面的n表示signal可以接收幾個參數,類型任意,最多為8個。這是由庫中指定的,當然如果實際開發需要更多的參數,可以修改sigslot庫。
slot是一個成員函數,它形如:
void SlotFunction(type1,type2……)
   需要記住:slot的類必須繼承has_slots<>;成員函數的返回值必須為void類型,這是這個庫的局限性,當然如果實際開發需要返回值,也是可以修改sigslot庫來實現。此外還需要注意的是slot的原形需要與sig一致。怎麼說呢,就是signal只能與帶有與它相同參數個數的slot函數進行綁定,而且signal的參數是直接傳遞給slot的。
 
4.    Sigslot庫用法
發送信號
信號(sig,即sig/slot的sig,下面提到的信號等同於此含義):
signal1<char *, int> ReportError;
比如上面的一個ReportError這個信號,當調用ReportError("Something went wrong", ERR_SOMETHING_WRONG);時候,將自動調用ReportError的emit成員函數發出一個信號。發給誰呢?
 
連接信息號
通過調用sig的connect函數建立sig和slot間的對應關係。Connect函數接收兩個參數,一個是消息目的對象的地址(指針),另一個是目的對象的成員函數指針(slot)。為了讓整個機制有效運行,目的類必須從has_slots<>繼承,並且sig/slot參數類型必須一致。也可以將一個sig連接到多個slot上,這樣每次sig發出信號的時候,每個連接的slot都能收到該信號。
 
斷開信號連接
通過調用sig的disconnect函數斷開sig和slot之間的連接,只有一個參數:目的對象的地址。一般不需要顯式調用disconnect函數,在sig類和目的類(包含slot函數的類)析構函數中將自動調用disconnect斷開sig和slot的連接。也可使用disconnect_all斷開該sig的所有slot。
all_on.Clicked.connect(&lp1,&Light::TurnOn);
all_on.Clicked.connect(&lp2,&Light::TurnOn);//同上
all_on.Clicked.disconnect_all();
 

沒有留言:

張貼留言