C++: Les signaux facile

Class signal

La programmation peut-être très très souvent adapté à la vie réelle.
Et ici, encore une fois, je vais pouvoir sortir un exemple de la vie courante, pour aider à mieux comprendre le principe des signaux.

Mais globalement, les signaux permettent d’émettre des messages vers une méthode (d’une classe ou non) pour signaler quelque chose, donc, travailler avec des évènements, au travers des boost::signal<> et sigc::signal<> ! :)

L’exemple de la vie courante

Imaginez que vous commandiez une pièce, pour votre voiture, à votre garagiste.

Soit le garagiste vous donne le délai et vous attendez ce délai quoi qu’il arrive, soit vous lui demandez qu’il vous appelle quand la pièce est prête.

Le garagiste va donc vous appeler / vous transmettre un message / vous signaler que le moment attendu ( La pièce est prête ) est arrivé.

Là, les signaux vont vous permettre de faire la même chose.

Vous créez un signal, pour annoncer que quelque chose de bien définis vient de se produire ( le signale à été appelé ), et émettez ce signal à la fonction connecté à ce signale.

Par exemple, vous pourriez créer un signal qui émettrait lorsqu’un booléen change d’état, ou encore lorsqu’une méthode de la classe à été appelé etc …

Les outils à disposition pour coder un signal

Pour coder vos signaux, il existe 2 Framework ( sous entendu, très connu ) :

La manière de coder des signaux, avec les deux Framework, est très proche. Donc, pour faire votre choix, si vous utilisez l’un des deux, autant continuer avec, pas la peine d’alourdir vos dépendances pour rien.

Passons au code !

Comme je l’ai dis juste avant, coder les signaux est très similaire pour ces deux Framework. Donc, je vais vous montrer la syntaxe pour les deux simultanément pour les deux cas.

Inclusion

GTKmm:

#include <sigc++/sigc++.h>

Boost:

#include <boost/signal.hpp>

Déclaration d’un signal

Dans les deux cas, il faut déclarer le signal. Cette étape permet de définir le typage des arguments que le signal pourra passer lorsqu’il sera émit.

Déclaration d’un signal sans arguments

GTKmm:

public:
    typedef sigc::signal<void> nomDuSignal;
protected:
    nomDuSignal                signalEmit;

Boost:

public:
    typedef boost::signal<void()> nomDuSignal;
private:
    nomDuSignal                   signalEmit;

Méthode attachable :

void MyClass::myMethod()
{
    std::cout << "MyClass::myMethod() called !" << std::endl;
}

Déclaration d’un signal avec arguments

GTKmm:

public:
    typedef sigc::signal<void, int> nomDuSignal;
protected:
    nomDuSignal                     signalEmit;

Boost:

public:
    typedef boost::signal<void(int)> nomDuSignal;
private:
    nomDuSignal                      signalEmit;

Méthode attachable :

void MyClass::myMethod( int i )
{
    std::cout << "MyClass::myMethod(" << i << ") called !" << std::endl;
}

Je trouve plus clair la version Boost.

Connexion du signal

Maintenant que notre signal est déclaré, ainsi que la méthode recevant le message émit, il nous faut les connecter ensemble !

Pour se faire, il faut créer une méthode dans la classe émettant le signale, qui va retourner la connexion établie, et ceux dans les deux cas: GTKmm et Boost.

Écrivons cette méthode:

GTKmm:

Emiter::nomDuSignal Emiter::signalEmitor()
{
    return signalEmit;
}

Boost:

boost::signals::connection Emiter::connect(nomDuSignal::slot_function_type subscriber)
{
    return signalEmit.connect(subscriber);
}

Puis il ne reste plus qu’à connecter.

Connexion du signal sans arguments

GTKmm:

instance_classe.signalEmitor().connect( sigc::ptr_fun( &nomMethod ) );

Boost:

instance_classe.connect( boost::bind( &nomMethod ) );

Connexion du signal avec arguments

Là GTKmm ne change pas la manière de connecter le signal, mais par contre, elle change pour Boost.

Boost:

instance_classe.connect( boost::bind( &dir, _1 ) );

Il faut rajouter une représentation des arguments. _1 pour le premier, _2 pour le second etc…

Maintenant, si vous exécutez le signal, la fonction connecté au signal sera appelé ! :-)

Les exemples

Bien entendu, pour vous aider à comprendre tout ca, j’ai écris 2 mini projets, utilisant les signaux avec GTKmm puis avec Boost.

Les sources se trouvent dans ce depot Git sur Github.com.