【问题标题】:Complete example using Boost::Signals for C++ Eventing使用 Boost::Signals 进行 C++ 事件的完整示例
【发布时间】:2010-10-20 13:56:38
【问题描述】:

我知道 boost.org 上的教程解决了这个问题: Boost.org Signals Tutorial,但示例并不完整,而且有些过于简化。那里的示例没有显示包含文件,并且代码的某些部分有点模糊。

这是我需要的:
ClassA 引发多个事件/信号
ClassB 订阅这些事件(可以订阅多个类)

在我的项目中,我有一个较低级别的消息处理程序类,该类将事件引发到业务类,该业务类对这些消息进行一些处理并通知 UI (wxFrames)。我需要知道这些都是如何连接起来的(什么顺序,谁打电话给谁,等等)。

【问题讨论】:

    标签: c++ boost-signals


    【解决方案1】:

    下面的代码是您所要求的最小工作示例。 ClassA 发出两个信号; SigA 不发送(并接受)任何参数,SigB 发送 intClassB 有两个函数,每个函数被调用时都会输出到cout。在示例中,有一个 ClassA (a) 实例和两个 ClassB (bb2) 实例。 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);
    }
    

    输出:

    富 酒吧:4 酒吧:4

    为简洁起见,我采用了一些您通常不会在生产代码中使用的快捷方式(特别是访问控制松懈,您通常会将信号注册“隐藏”在 KeithB 示例中的函数后面)。

    似乎boost::signal 的大部分困难在于习惯使用boost::bind。一开始有点令人费解!对于一个更棘手的示例,您还可以使用bindClassA::SigAClassB::PrintInt 挂钩,即使SigA 确实 发出int

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

    希望有帮助!

    【讨论】:

    • 是否可以重载函数,如果可以,您是否介意添加它。英石。你有类似 PrintNum(int);和 PrintNum(float);
    • @pyInTheSky 使用函数类型(不确定确切的术语):(void (*)(int))&amp;PrintNum
    【解决方案2】:

    这是我们代码库中的一个示例。它被简化了,所以我不保证它会编译,但它应该很接近。 Sublocation 是您的 A 类,而 Slot1 是您的 B 类。我们有许多这样的插槽,每个插槽都订阅不同的信号子集。使用这种方案的优点是 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;
    };
    

    子位置.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)
    {
      // ...
    }
    

    【讨论】:

    • 很好的例子。虽然我认为 - 相当强烈 - 信号应该是可变的,而 addSignalXXX 应该是 const。它们是公共接口的一部分,肯定会改变 SubLocation 的行为。
    • 我认为这一点值得商榷。我能理解你来自哪里。另一方面,添加插槽不会直接更改任何子位置状态。并且如果 slot 在被调用时想要改变状态,它必须调用 sublocation 的非常量成员函数。如果这是在代码审查中提出的,我会陈述我的情况,但如果这是共识,我不介意做出您建议的更改。
    • 我也能看到你的论点……这可能是一个有趣的代码审查讨论。 :)
    【解决方案3】:

    【讨论】:

    • 谢谢!这些例子稍微好一点,但如果有一个更大、更真实的例子就好了。
    【解决方案4】:

    类似 QT 的 Boost 提供了自己的信号和插槽实现。以下是其实现的一些示例。

    命名空间的信号和插槽连接

    考虑一个名为 GStreamer 的命名空间

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

    这里是如何创建和触发信号

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

    类的信号和插槽连接

    考虑一个名为 GSTAdaptor 的类,其函数名为 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
    

    【讨论】:

      【解决方案5】:

      当使用更新的 boost (f.e. 1.61) 编译 MattyT 的示例时,它会发出警告

      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;
      

      【讨论】:

        【解决方案6】:

        上面的答案很好用 signal2 相同的答案应该重写:

        #include <boost/signals2.hpp>
        #include <boost/bind.hpp>
        #include <iostream>
        
        using namespace boost;
        using namespace std;
        
        struct ClassA
        {
            signals2::signal<void ()>    SigA;
            signals2::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);
        }
        

        【讨论】:

          猜你喜欢
          • 2011-06-18
          • 1970-01-01
          • 1970-01-01
          • 2010-11-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-22
          • 2010-10-15
          相关资源
          最近更新 更多