Завершите пример, используя 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)
{
// ...
}
При компиляции примера 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);
}