2009-04-20 13 views
54

Ich kenne das Tutorial bei boost.org adressiert dies: Boost.org Signals Tutorial, aber die Beispiele sind nicht vollständig und etwas über vereinfacht. Die Beispiele dort zeigen nicht die Include-Dateien und einige Abschnitte des Codes sind ein wenig vage. HierKomplettes Beispiel mit Boost :: Signale für C++ Eventing

ist das, was ich brauche:
KlasseA mehrere Ereignisse erhöht/Signale
ClassB auf diese Ereignisse abonniert (Mehrere Klassen abonnieren kann)

In meinem Projekt habe ich eine untergeordnete Message-Handler-Klasse, löst Ereignisse in einer Business-Klasse aus, die diese Nachrichten verarbeitet und die Benutzeroberfläche benachrichtigt (wxFrames). Ich muss wissen, wie diese allesamt verdrahtet werden können (welche Reihenfolge, wer wer anruft usw.).

Antwort

82

Der folgende Code ist ein minimales Arbeitsbeispiel für das, was Sie angefordert haben. ClassA gibt zwei Signale aus; SigA sendet (und akzeptiert) keine Parameter, SigB sendet eine int. ClassB hat zwei Funktionen, die beim Aufruf jeder Funktion an cout ausgegeben werden. Im Beispiel gibt es eine Instanz von ClassA (a) und zwei von ClassB (b und b2). main wird verwendet, um die Signale zu verbinden und zu zünden. Es ist erwähnenswert, dass ClassA und ClassB nichts voneinander wissen (dh sie sind nicht Kompilierzeit gebunden).

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

Der Ausgang:

 
Foo 
Bar: 4 
Bar: 4 

Der Kürze halber ich einige Abkürzungen genommen haben, die Sie normalerweise nicht in Produktionscode (insbesondere Zugangskontrolle verwenden würde, ist lax, und Sie würden normalerweise ‚verstecken‘ Ihre Signalregistrierung hinter einer Funktion wie im Beispiel von KeithB).

Es scheint, dass der größte Teil der Schwierigkeit in boost::signal sich an die Verwendung von boost::bind zu gewöhnen ist.Es ist ein bisschen verrückt zuerst! Für ein heikler Beispiel könnten Sie auch bind verwenden ClassA::SigA mit ClassB::PrintInt sogar anschließen obwohl SigA tut nicht emittieren eine int:

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

Hoffnung, das hilft!

+0

Ist es möglich, eine Funktion zu überlasten, und wenn ja, würde es Ihnen etwas ausmachen, das hinzuzufügen. s.t. Sie haben etwas wie PrintNum (int); und PrintNum (float); – pyInTheSky

+0

@pyInTheSky Verwenden Sie einen Funktionstyp (nicht sicher über den genauen Begriff): '(void (*) (int)) & PrintNum' – Qix

6

Haben Sie sich boost/libs/signals/example angesehen?

+0

Dank! diese Beispiele sind ein wenig besser, aber es wäre schön, ein größeres, realistischeres Beispiel zu haben. –

11

Hier ist ein Beispiel aus unserer Codebasis. Es wurde vereinfacht, also kann ich nicht garantieren, dass es kompiliert wird, aber es sollte nah sein. Sublokation ist Ihre Klasse A und Slot1 ist Ihre Klasse B. Wir haben eine Anzahl von Slots wie diese, von denen jeder eine andere Teilmenge von Signalen abonniert. Die Vorteile bei der Verwendung dieses Schemas liegen darin, dass die Sublocation nichts über die Slots weiß und die Slots nicht Teil einer Vererbungshierarchie sein müssen und nur Implementierungsfunktionen für die Slots benötigen, die ihnen wichtig sind. Wir verwenden dies, um benutzerdefinierte Funktionalität in unser System mit einer sehr einfachen Schnittstelle hinzuzufügen.

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

Gutes Beispiel. Allerdings würde ich - ziemlich stark - argumentieren, dass die Signale * nicht * änderbar sein sollten und addSignalXXX * nicht * const sein sollte. Sie sind Teil der öffentlichen Schnittstelle und ändern definitiv das Verhalten von SubLocation. – MattyT

+0

Ich denke, dass dieser Punkt umstritten ist. Ich kann verstehen, woher du kommst. Auf der anderen Seite ändert das Hinzufügen eines Zeitschlitzes keinen Sublokationsstatus direkt. Und wenn der Slot seinen Zustand ändern will, wenn er aufgerufen wird, muss er nonconst-Mitgliedsfunktionen der Sublokation aufrufen. Wenn dies in einem Code-Review auftaucht, würde ich meinen Fall darlegen, würde aber nichts dagegen haben, die von Ihnen vorgeschlagene Änderung vorzunehmen, wenn dies der Konsens wäre. – KeithB

+1

Ich kann Ihre Argumentation auch sehen ... es wäre wahrscheinlich eine interessante Code-Review-Diskussion. :) – MattyT

1

Boost wie QT bietet eine eigene Implementierung von Signalen und Slots. Im Folgenden finden Sie ein Beispiel für die Implementierung.

Signal- und Slot-Verbindung für Namespace

einen Namespace Betrachten genannt GStreamer

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

Hier ist, wie das Signal zu erzeugen und auslösen

#include<boost/signal.hpp> 

... 

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

Signal- und Slot Anschluss für eine Klasse

Betrachten Sie eine Klasse namens GSTAdaptor mit Spaß ction genannt func1 und func2 mit Unterschrift folgenden

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

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

Hier ist, wie das Signal

#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

erstellen und auslösen Wenn MattyT das Beispiel mit neueren Boost-Kompilierung (F. E. 1,61), dann gibt es eine Warnung

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

Also entweder Sie BOOST_SIGNALS_NO_DEPRECATION_WARNING definieren die Warnung zu unterdrücken, oder Sie können wechseln leicht entsprechend, indem das Beispiel boost.signal2:

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

using namespace boost::signals2; 
using namespace std;