2009-04-20 86 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;