Завершите пример, используя Boost::Signals для C++ Eventing

Я знаю об учебнике на boost.org, посвященном этому: Boost.org Signals Tutorial, но примеры не полны и несколько упрощены. Приведенные здесь примеры не показывают включаемые файлы, а некоторые разделы кода немного расплывчаты.

Вот что мне нужно:
ClassA вызывает несколько событий / сигналов
ClassB подписывается на эти события (несколько классов могут подписаться)

В моем проекте у меня есть класс обработчика сообщений более низкого уровня, который вызывает события для бизнес-класса, который выполняет некоторую обработку этих сообщений и уведомляет пользовательский интерфейс (wxFrames). Мне нужно знать, как все это может быть организовано (в каком порядке, кто звонит и т.д.).

6 ответов

Решение

Код ниже является минимальным рабочим примером того, что вы просили. ClassA испускает два сигнала; SigA не отправляет (и не принимает) никаких параметров, SigB отправляет int, ClassB имеет две функции, которые будут выводить на cout когда каждая функция вызывается. В примере есть один экземпляр ClassA (a) и два из ClassB (b а также b2). main используется для подключения и запуска сигналов. Стоит отметить, что ClassA а также ClassB ничего не знают друг о друге (т.е. они не связаны во время компиляции).

#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
Бар: 4
Бар: 4

Для краткости я взял несколько ярлыков, которые вы обычно не используете в производственном коде (в частности, контроль доступа слабый, и вы обычно "скрываете" свою регистрацию сигнала за функцией, как в примере с KeithB).

Кажется, что большая часть трудностей в boost::signal в привыкании к использованию boost::bind, Сначала это немного сногсшибательно! Для более сложного примера вы также можете использовать bind застегнуть ClassA::SigA с ClassB::PrintInt даже если SigA не испускает int:

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

Надеюсь, это поможет!

Вот пример из нашей кодовой базы. Он был упрощен, поэтому я не гарантирую, что он скомпилируется, но он должен быть близок. Выделение - это ваш класс 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)
{
  // ...
}

Вы смотрели на boost/libs/ сигналов / пример?

При компиляции примера 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;

Boost like QT обеспечивает собственную реализацию сигналов и слотов. Ниже приведены некоторые примеры его реализации.

Соединение Signal и Slot для пространства имен

Рассмотрим пространство имен под названием 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

Вышеуказанный ответ отлично подходит для 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);
}
Другие вопросы по тегам