63 votes

Exemple complet utilisant Boost::Signals pour le C++ Eventing

Je connais le tutoriel de boost.org qui traite de ce sujet : Tutoriel sur les signaux Boost.org mais les exemples ne sont pas complets et sont quelque peu simplifiés. Les exemples ne montrent pas les fichiers include et certaines sections du code sont un peu vagues.

Voici ce dont j'ai besoin :
La classe A soulève plusieurs événements/signaux
La classe B s'abonne à ces événements (plusieurs classes peuvent s'abonner).

Dans mon projet, j'ai une classe de gestion des messages de niveau inférieur qui envoie des événements à une classe commerciale qui traite ces messages et notifie l'interface utilisateur (wxFrames). J'ai besoin de savoir comment tout cela peut être connecté (dans quel ordre, qui appelle qui, etc.).

94voto

MattyT Points 3195

Le code ci-dessous est un exemple minimal de ce que vous avez demandé. ClassA émet deux signaux ; SigA n'envoie (et n'accepte) aucun paramètre, SigB envoie un int . ClassB dispose de deux fonctions qui renvoient vers cout lorsque chaque fonction est appelée. Dans l'exemple, il y a une instance de ClassA ( a ) et deux des ClassB ( b y b2 ). main est utilisé pour connecter et déclencher les signaux. Il convient de noter que ClassA y ClassB ne se connaissent pas (c'est-à-dire qu'ils ne sont pas liée au moment de la compilation ).

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

Le résultat :

Foo
Bar: 4
Bar: 4

Par souci de concision, j'ai pris quelques raccourcis que vous n'utiliseriez pas normalement dans un code de production (en particulier, le contrôle d'accès est laxiste et vous "cacheriez" normalement votre enregistrement de signal derrière une fonction comme dans l'exemple de KeithB).

Il semble que la plupart des difficultés boost::signal est de s'habituer à utiliser les boost::bind . Il es un peu déroutant au début ! Pour un exemple plus délicat, vous pouvez également utiliser bind de s'accrocher ClassA::SigA con ClassB::PrintInt même si SigA fait pas émet un int :

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

0 votes

Est-il possible de surcharger une fonction, et si oui, pourriez-vous l'ajouter ? Par exemple, vous avez quelque chose comme PrintNum(int) ; et PrintNum(float) ;

0 votes

@pyInTheSky Utilisez un type de fonction (je ne suis pas sûr du terme exact) : (void (*)(int))&PrintNum

11voto

KeithB Points 9459

Voici un exemple tiré de notre base de données. Il a été simplifié, donc je ne garantis pas qu'il sera compilé, mais il devrait être proche. Sublocation est votre classe A, et Slot1 est votre classe B. Nous avons un certain nombre de slots comme celui-ci, chacun souscrivant à un sous-ensemble différent de signaux. L'avantage de ce schéma est que Sublocation ne sait rien des slots, et que les slots n'ont pas besoin de faire partie d'une hiérarchie d'héritage, et n'ont besoin d'implémenter des fonctionnalités que pour les slots dont ils s'occupent. Nous l'utilisons pour ajouter des fonctionnalités personnalisées à notre système avec une interface très 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;
};

Sous-localisation.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 votes

C'est un bon exemple. Cependant, je dirais - assez fermement - que les signaux devraient pas doit être mutable et addSignalXXX doit pas être constante. Ils font partie de l'interface publique et modifient définitivement le comportement de SubLocation.

0 votes

Je pense que ce point est discutable. Je peux comprendre votre point de vue. D'un autre côté, l'ajout d'un slot ne modifie pas directement l'état d'une sous-localisation. Et si le slot veut changer d'état lorsqu'il est appelé, il doit appeler des fonctions membres nonconst de la sous-localisation. Si cette question était soulevée lors d'une revue de code, j'exposerais mon point de vue, mais cela ne me dérangerait pas de faire le changement que vous avez suggéré si cela faisait l'objet d'un consensus.

1 votes

Je comprends aussi votre argument... cela donnerait probablement lieu à une discussion intéressante sur l'examen du code :)

6voto

Éric Malenfant Points 10082

Avez-vous regardé boost/libs/signaux/exemple ?

0 votes

Ces exemples sont un peu meilleurs, mais il serait bon d'avoir un exemple plus grand et plus réaliste.

2voto

psalong Points 136

Lorsque l'on compile l'exemple de MattyT avec un boost plus récent (par exemple 1.61), il y a un avertissement

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

Donc soit vous définissez BOOST_SIGNALS_NO_DEPRECATION_WARNING pour supprimer l'avertissement, soit vous pouvez facilement passer à boost.signal2 en modifiant l'exemple en conséquence :

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

using namespace boost::signals2;
using namespace std;

1voto

Vihaan Verma Points 1389

Boost, comme QT, fournit sa propre implémentation des signaux et des slots. Voici quelques exemples de cette implémentation.

Connexion de signal et de fente pour l'espace de noms

Considérons un espace de noms appelé GStreamer

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

Voici comment créer et déclencher le signal

 #include<boost/signal.hpp>

 ...

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

Connexion de signal et de fente pour une classe

Considérons une classe appelée GSTAdaptor avec des fonctions appelées func1 et func2 avec la signature suivante

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

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

Voici comment créer et déclencher le 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X