2009-04-20 74 views
54

我知道boost.org解決方案中的教程: Boost.org Signals Tutorial,但這些示例並不完整,並且有些過於簡化。那裏的例子沒有顯示包含文件,代碼的一些部分有些模糊。使用Boost :: Signals for C++ Eventing的完整示例

以下是我需要:
ClassA的引發多個事件/信號
ClassB的訂閱這些事件(多個類可以訂閱)

在我的項目我有一個較低的水平,消息處理程序類將事件引發到對這些消息進行一些處理並通知UI的業務類(wxFrames)。我需要知道這些所有可能如何連接(什麼順序,誰稱誰等)。

回答

82

下面的代碼是您所要求的最小工作示例。 ClassA發出兩個信號; SigA發送(並接受)無參數,SigB發送int。當調用每個函數時,ClassB有兩個函數將輸出到cout。在該示例中,存在ClassAa)的一個實例和ClassBbb2)中的兩個。 main用於連接和觸發信號。值得注意的是,ClassAClassB對彼此不知情(即它們不是編譯時綁定的)。

#include <boost/signal.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 

using namespace boost; 
using namespace std; 

struct ClassA 
{ 
    signal<void()> SigA; 
    signal<void (int)> SigB; 
}; 

struct ClassB 
{ 
    void PrintFoo()  { cout << "Foo" << endl; } 
    void PrintInt(int i) { cout << "Bar: " << i << endl; } 
}; 

int main() 
{ 
    ClassA a; 
    ClassB b, b2; 

    a.SigA.connect(bind(&ClassB::PrintFoo, &b)); 
    a.SigB.connect(bind(&ClassB::PrintInt, &b, _1)); 
    a.SigB.connect(bind(&ClassB::PrintInt, &b2, _1)); 

    a.SigA(); 
    a.SigB(4); 
} 

輸出:

 
Foo 
Bar: 4 
Bar: 4 

爲了簡便起見,我採取了一些快捷鍵,你會不會在生產代碼正常使用(尤其是訪問控制不嚴和你通常「隱藏」你在KeithB例子中的函數背後的信號註冊)。

看來boost::signal的大部分難度都在習慣使用boost::bind。它起初有點心靈彎曲!對於一個更爲複雜的例子,你也可以使用bind掛鉤ClassA::SigAClassB::PrintInt即使SigA發出int

a.SigA.connect(bind(&ClassB::PrintInt, &b, 10)); 

希望幫助!

6
+0

謝謝!這些例子稍微好一些,但是有一個更大更實際的例子會很好。 – 2009-04-20 15:16:50

11

以下是我們的代碼庫示例。它被簡化了,所以我不保證它會編譯,但它應該是接近的。 Sublocation是您的A級,Slot1是您的B級。我們有一些像這樣的插槽,每個插槽都訂閱不同的信號子集。使用這種方案的優點在於,Sublocation不知道任何有關插槽的信息,插槽不需要成爲任何繼承層次結構的一部分,只需要爲他們關心的插槽實現功能。我們使用它通過非常簡單的界面將自定義功能添加到我們的系統中。

Sublocation.h

class Sublocation 
{ 
public: 
    typedef boost::signal<void (Time, Time)> ContactSignal; 
    typedef boost::signal<void()> EndOfSimSignal; 

    void endOfSim(); 
    void addPerson(Time t, Interactor::Ptr i); 

    Connection addSignalContact(const ContactSignal::slot_type& slot) const; 
    Connection addSignalEndOfSim(const EndOfSimSignal::slot_type& slot) const;  
private: 
    mutable ContactSignal fSigContact; 
    mutable EndOfSimSignal fSigEndOfSim; 
}; 

Sublocation.C

void Sublocation::endOfSim() 
{ 
    fSigEndOfSim(); 
} 

Sublocation::Connection Sublocation::addSignalContact(const ContactSignal::slot_type& slot) const 
{ 
    return fSigContact.connect(slot); 
} 

Sublocation::Connection Sublocation::addSignalEndOfSim(const EndOfSimSignal::slot_type& slot) const 
{ 
    return fSigEndOfSim.connect(slot); 
} 

Sublocation::Sublocation() 
{ 
    Slot1* slot1 = new Slot1(*this); 
    Slot2* slot2 = new Slot2(*this); 
} 

void Sublocation::addPerson(Time t, Interactor::Ptr i) 
{ 
    // compute t1 
    fSigOnContact(t, t1); 
    // ... 
} 

Slot1.h

class Slot1 
{ 
public: 
    Slot1(const Sublocation& subloc); 

    void onContact(Time t1, Time t2); 
    void onEndOfSim(); 
private: 
    const Sublocation& fSubloc; 
}; 

Slot1.C

Slot1::Slot1(const Sublocation& subloc) 
: fSubloc(subloc) 
{ 
    subloc.addSignalContact(boost::bind(&Slot1::onContact, this, _1, _2)); 
    subloc.addSignalEndSim(boost::bind(&Slot1::onEndSim, this)); 
} 


void Slot1::onEndOfSim() 
{ 
    // ... 
} 

void Slot1::onContact(Time lastUpdate, Time t) 
{ 
    // ... 
} 
+0

很好的例子。儘管我認爲 - 相當強烈 - 信號不應該是可變的,並且addSignalXXX應該不是const。它們是公共接口的一部分,並且肯定會改變SubLocation的行爲。 – MattyT 2009-04-21 00:57:30

+0

我認爲這一點是有爭議的。我可以理解你來自哪裏。另一方面,添加插槽不會直接更改任何子位置狀態。如果插槽希望在被調用時改變狀態,它必須調用非常量成員函數的子位置。如果這是在代碼審查中提出的,我會說明我的情況,但如果這是共識,則不介意進行您提出的更改。 – KeithB 2009-04-21 15:21:03

1

類似QT的Boost提供了自己的信號和插槽實現。以下是其實施的一些例子。

信號和命名空間

插槽連接考慮一個名爲GStreamer的命名空間

namespace GStremer 
{ 
    void init() 
    { 
    .... 
    } 
} 

這裏是如何創建和觸發信號

#include<boost/signal.hpp> 

... 

boost::signal<void()> sigInit; 
sigInit.connect(GStreamer::init); 
sigInit(); //trigger the signal 

信號和一類

插槽連接

考慮一個名爲GSTAdaptor的類,帶着樂趣ction稱爲FUNC1和FUNC2具有以下簽名

void GSTAdaptor::func1() 
{ 
... 
} 

void GSTAdaptor::func2(int x) 
{ 
... 
} 

這裏是如何創建和觸發信號

#include<boost/signal.hpp> 
#include<boost/bind.hpp> 

... 

GSTAdaptor g; 
boost::signal<void()> sigFunc1; 
boost::signal<void (int)> sigFunc2; 

sigFunc1.connect(boost::bind(&GSTAdaptor::func1, &g); 
sigFunc2.connect(boost::bind(&GSTAdaptor::func2, &g, _1)); 

sigFunc1();//trigger the signal 
sigFunc2(6);//trigger the signal 
0

編譯時使用較新的提升MattyT的例子指標(如1.61),那麼它會發出警告

error: #warning "Boost.Signals is no longer being maintained and is now deprecated. Please switch to Boost.Signals2. To disable this warning message, define BOOST_SIGNALS_NO_DEPRECATION_WARNING." 

因此,無論你定義BOOST_SIGNALS_NO_DEPRECATION_WARNING相應的警告或者你可以方便地切換到boost.signal2通過相應地改變例如:

#include <boost/signals2.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 

using namespace boost::signals2; 
using namespace std;