2009-04-20 15 views
54

Estoy al tanto del tutorial en boost.org dirigiéndome a esto: Boost.org Signals Tutorial, pero los ejemplos no están completos y algo simplificados. Los ejemplos allí no muestran los archivos de inclusión y algunas secciones del código son un poco vagas.Ejemplo completo usando Boost :: Signals for C++ Eventing

Esto es lo que necesito:
claseA plantea múltiples eventos/señales
ClassB suscriba a esos eventos (múltiples clases pueden suscribirse)

En mi proyecto tengo una clase controlador de mensajes de nivel inferior que plantea eventos a una clase de negocios que procesa esos mensajes y notifica a la IU (wxFrames). Necesito saber cómo se pueden conectar todos estos (qué orden, quién llama a quién, etc.).

Respuesta

82

El siguiente código es un ejemplo mínimo de trabajo de lo que solicitó. ClassA emite dos señales; SigA envía (y acepta) sin parámetros, SigB envía un int. ClassB tiene dos funciones que darán salida a cout cuando se llame a cada función. En el ejemplo, hay una instancia de ClassA (a) y dos de ClassB (b y b2). main se usa para conectar y disparar las señales. Vale la pena señalar que ClassA y ClassB no saben nada el uno del otro (es decir, no están enlazados en tiempo de compilación).

#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); 
} 

La salida:

 
Foo 
Bar: 4 
Bar: 4 

Por razones de brevedad que he tomado algunos accesos directos que no usaría normalmente en el código de producción (en particular, el control de acceso es laxa y que le normalmente 'ocultar' su registro de señal detrás de una función como en el ejemplo de KeithB).

Parece que la mayor parte de la dificultad en boost::signal es acostumbrarse al uso de boost::bind.Es es un poco alucinante al principio! Para un ejemplo más complicado también se puede utilizar para conectar bindClassA::SigA con ClassB::PrintInt a pesar de que hace SigA no emiten una int:

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

Espero que ayude!

+0

¿Es posible sobrecargar una función y, de ser así, le importaría agregar eso? S t. tienes algo como PrintNum (int); y PrintNum (flotante); – pyInTheSky

+0

@pyInTheSky Use un tipo de función (no estoy seguro del término exacto): '(void (*) (int)) & PrintNum' – Qix

6
+0

Gracias! estos ejemplos son un poco mejores, pero sería bueno tener un ejemplo más grande y más realista. –

11

Aquí hay un ejemplo de nuestra base de código. Se ha simplificado, por lo que no garantizo que compilará, pero debería estar cerca. La sublocación es su clase A, y Slot1 es su clase B. Tenemos un número de espacios como este, cada uno de los cuales se suscribe a un subconjunto diferente de señales. Las ventajas de utilizar este esquema son que Sublocation no sabe nada de ninguno de los slots, y los slots no necesitan formar parte de ninguna jerarquía de herencia, y solo necesitan implementar funcionalidades para los slots que les interesan. Usamos esto para agregar funcionalidad personalizada a nuestro sistema con una interfaz muy simple.

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

Buen ejemplo. Aunque argumentaría, bastante fuerte, que las señales no deberían * ser * mutables y addSignalXXX no debería * ser * const. Son parte de la interfaz pública y definitivamente cambian el comportamiento de SubLocation. – MattyT

+0

Creo que este punto es discutible. Puedo entender de donde vienes. Por otro lado, agregar una ranura no cambia ningún estado de sublocación directamente. Y si la ranura desea cambiar de estado cuando se llama, debe llamar a las funciones de subconjunto de miembros que no son de enlace. Si esto se mencionara en una revisión del código, expondría mi caso, pero no me importaría hacer el cambio que sugirió si ese fuera el consenso. – KeithB

+1

Puedo ver tu argumento también ... probablemente sea una discusión interesante sobre la revisión del código. :) – MattyT

1

Boost como QT proporciona su propia implementación de señales y ranuras. Los siguientes son algunos ejemplos de su implementación.

señal y ranura para espacio de nombres

Considere un espacio de nombres llamado GStreamer

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

Aquí es cómo crear y activar la señal

#include<boost/signal.hpp> 

... 

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

señal y ranura para una clase

Considere una clase llamada GSTAdaptor con diversión cción llama func1 y func2 con firma siguiente

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

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

Aquí es cómo crear y activar la señal

#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

Al compilar el ejemplo de MattyT con nueva alza (F. E. 1,61), entonces se da una advertencia

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." 

Así que, o se define BOOST_SIGNALS_NO_DEPRECATION_WARNING suprimir la advertencia o que fácilmente podría cambiar a boost.signal2 cambiando el ejemplo en consecuencia:

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

using namespace boost::signals2; 
using namespace std; 
Cuestiones relacionadas