Gestion (ou simulation) des évenements en c++ pure

Soyez le premier à donner votre avis sur cette source.

Vue 13 673 fois - Téléchargée 509 fois

Description

\o/ Bonjour tout le monde \o/ :)

Ce code source traite de la gestion (quasi inexistante) des évènements en C++ pure.
Je n'ai jamais trouvé de façon de gérer des évènements (ou "callbacks" comme certains les appellent)
proprement et sans prise de tête
J'ai donc voulu faire une gestion des évènements basée sur des MACROs.
Cette gestion s'inspire de ce que j'ai pu voir :
Qt
C#
Borland c++ (__closure)
java.

Toute critique constructive, bonne ou mauvaise, est bienvenue.

Voici donc un code commenté (le contenu du fichier example.cpp dans le zip), très basique, qui utilise mon magic header de gestion des évènements :

Sujet :

On veut contrôler les changements de valeur d'une classe Integer qui encapsule un entier.
La classe Integer va donc déclarer un évènement OnCanChange qui prendra en argument :
--> la valeur courante de l'entier encapsulé
--> la nouvelle valeur à affecter l'entier encapsulé
--> un booleen indiquant si le changement doit être fait.

Une autre classe (KeepEven) va donc donc contrôler les changements de l'entier
en s'abonnant à l'évènement de l'instance d'Integer.
Cette classe va simplement garder les changements qui conduise l'entier à devenir ou rester pair.

Source / Exemple :


#include <list>
#include <iostream>

// mon header magique :)
#include "Event.hpp"

/// Cette liste lève un évènement à chaque ajout / suppression d'un élément.
template<typename T>
class List
{
	// Indique que la classe peut émettre des évènements.
	EMITER(List<T>);

public :

	// Déclare en quelque sorte les signatures que l'on veut pour
	// les évènements.
	struct EventArgs      : public Event::Args {       T* object; };
	struct EventConstArgs : public Event::Args { const T* object; };

	// Déclaration des évènements
	event<EventConstArgs> on_const_add;
	event<EventArgs>      on_add;
	event<EventArgs>      on_remove;

	// liste encapsulée
	std::list<T> lst;

	void length() { return lst.length(); }
	const T& operator[] (int index) const { return lst[index]; }
	      T& operator[] (int index)       { return lst[index]; }

	void add(T& object)
	{
		// Déclare les arguments
		EventArgs args; args.object = &object;
		// Emet l'evenement
		on_add(args);
		// Effectue l'ajout
		lst.push_back(object);
	}
	void add(const T& object)
	{
		EventConstArgs args; args.object = &object;
		on_const_add(args);
		lst.push_back(object);
	}

	void remove_at(int index)
	{
		typename std::list<T>::iterator it = lst.begin();
		std::advance(it, index);
		EventArgs args; args.object = &(*it);
		on_remove(args);
		lst.erase(it);
	}
};

// Cette classe permet simplement de logguer des instances de List
template<typename T>
class ChangeNotifier
{
	// Classe qui est reellement instancié.
	class Notifier
	{
		// Indique que la classe peut s'abonner à des évènements.
		RECEIVER(Notifier);

	public: // Les trois méthodes qui reçoivent les évènements.

		void SLOT(onConstAdd)(typename List<T>::EventArgs* args)
		{
			std::cout << "Adding object : " << *args->object << std::endl;
		}
		void SLOT(onAdd)(typename List<T>::EventArgs* args)
		{
			onConstAdd(args);
		}
		void SLOT(onRemove)(typename List<T>::EventArgs* args)
		{
			std::cout << "Removing object : " << *args->object << std::endl;
		}
	};

	static Notifier& notifier() { static Notifier _; return _; }

public:

	// Abonne le notifier aux évènements de la classe
	static void watch(List<T>& container)
	{
		container.on_add       += CLOSURE(notifier(), onAdd);
		container.on_const_add += CLOSURE(notifier(), onConstAdd);
		container.on_remove    += CLOSURE(notifier(), onRemove);
	}

	// Le désabonne..
	static void stop_watch(List<T>& container)
	{
		container.on_add       -= CLOSURE(notifier(), onAdd);
		container.on_const_add -= CLOSURE(notifier(), onConstAdd);
		container.on_remove    -= CLOSURE(notifier(), onRemove);
	}
};

int main()
{
	List<int> my_watched_list;
	ChangeNotifier<int>::watch(my_watched_list);

	my_watched_list.add(42);
	my_watched_list.add(51);
	my_watched_list.remove_at(0);
	my_watched_list.remove_at(0);

	ChangeNotifier<int>::stop_watch(my_watched_list);
}

Conclusion :


Soyons clair avec les choses génantes :
--> Les MACROs font du travail qui peut sembler sale derrière ... !
--> Les MACROs déclarent des choses et l'autocompletion peut les faire apparaitre.
Je pense notemment aux IDEs qui traitent les MACROs comme Eclipse.
N'utiliser donc pas tous ce qui commence par deux underscores.
--> Il est certainement possible que cela ne compile / fonctionne pas sur des archis spécifiques.
Dans ce cas là, n'hésitez pas à me le faire savoir !
--> Si vous vous plantez en écrivant votre code, les erreurs ne seront evidement pas explicites car elles "parleront" du code généré ...

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

Messages postés
370
Date d'inscription
lundi 1 avril 2002
Statut
Membre
Dernière intervention
11 février 2010

Mise à jour:
-- Le code est un peu plus solide et complet.
-- Nouvel exemple un peu plus simple avec la classe "List", visible en ligne.

xydion :
J'ai surtout ajouté des templates pour vérifier la signature de la fonction transmise à SLOT.
Donc j'ai corrigé dans le fichier "events_example.cpp". Le problème ne devrait plus se reproduire car
le compilateur sort une erreur assez explicite je pense :)

Prochaine étape : utilisation multi threaded
Messages postés
1
Date d'inscription
lundi 29 septembre 2008
Statut
Membre
Dernière intervention
16 février 2010

Il y a une petite erreur dans le code source.
Le SLOT OnIntegerCanChange prend un pointeur en paramètre donc:
Remplacer par :
void SLOT(OnIntegerCanChange)(CanChangeEventArgs* args)
{args->accept (args->new_value%2 0);
}
Messages postés
475
Date d'inscription
jeudi 19 juin 2003
Statut
Membre
Dernière intervention
3 novembre 2008

Belle source, il reste maintenant juste à ajouter le support thread pour des événements non-bloquants :p
Messages postés
1910
Date d'inscription
vendredi 18 juin 2004
Statut
Modérateur
Dernière intervention
14 novembre 2014
12
yann_lo_san >> J'ai supprimé le "6/10". Tu peux renoter.
Messages postés
1137
Date d'inscription
lundi 17 novembre 2003
Statut
Membre
Dernière intervention
23 janvier 2016
18
Désolé pour le 6/10, si je pouvait renoter je mettrais plutot 8/10 (pour le .hh avec templates).
Bonne continuation.
Afficher les 13 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.