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

0/5 (13 avis)

Vue 14 820 fois - Téléchargée 542 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
Arnaud16022 Messages postés 1329 Date d'inscription vendredi 15 août 2003 Statut Membre Dernière intervention 16 juin 2010 2
16 nov. 2006 à 12:44
atta, j'ai un pb là...
"gestion des callbacks proprement et sans prise de tête"
et tu dois encapsuler un int ?

Dans ton main, je ne vois réellement pas l'intérêt de ce que tu fais ... tu n'as pas un exemple un peu plus explicite ? parce que j'ai du mal à m'imaginer en quoi tout ton prog est mieux que
int integer = 42; std::cout << integer ;

Voilà, je suis désolé de ne pas saisir la subtilité/ utilité de ton prog ... le pire c'est que je suis sûr qu'on fond c'est pas stupide du tout :p
need explanations :'(
yann_lo_san Messages postés 1137 Date d'inscription lundi 17 novembre 2003 Statut Membre Dernière intervention 23 janvier 2016 24
16 nov. 2006 à 14:30
Même remarque que Arnaud16022, il suffit pour ton exemple d'un (nombre % 2 == 0) pour pair/impair.
Donc un exemple plus explicite serait le bienvenue.
Mais demandons nous pourquoi cela n'a pas été implémenté dans le standart C++ à l'instar de Java et C# ?
Sinon, bonne utilisation classes, je te mets 6.
Bonne continuation...
cs_Zeroc00l Messages postés 367 Date d'inscription lundi 1 avril 2002 Statut Membre Dernière intervention 11 février 2010
16 nov. 2006 à 14:59
Lol, vous savez ce que sont les évènements :p ?

J'ai l'impression que vous n'avez pas compris que ces sources sont un exemple.
Et donc comme la majorité des choses que l'on apprend, on démarre avec des exemples très simples, où la signification concrete et la manipulation des objets n'ont pas à fatiguer l'esprit de la personne qui le lit.

Bien sur que manipuler un entier (int) au travers d'une classe est inutile.
Mais cela permet à tout le monde de se faire une idée de ce à quoi devrait ressembler le code, avant même qu'il ne l'ai lu. De plus, un entier étant une chose tellement commune, je pense que tout le monde penserait à la même implémentation (ou presque).

Bien sur que vérifier qu'un nombre est pair est très simple !
Même raison qu'au dessus.

Si j'avais pris un exemple avec mes idées loufoques (On a tous nos idées loufoques ;), non seulement il aurait fallu que vous compreniez ce qu'à la base je voulais coder et ensuite seulement la façon dont je l'ai fait, imaginé, avec les macros que je présente ici.

Pour finir, j'insiste sur le fait que j'ai justement codé quelquechose qui soit COMPLETEMENT compréhensible, à tel point qu'au final la seule chose qui importe vraiment soit la façon de l'écrire, non la façon de le penser en terme de modélisation.

J'encourage donc tout le monde à avoir recours à ce genre d'exemple, et espère que je ne suis pas seul à penser ça !



Mais puisque la remarque m'a été faite, qu'on dialogue entre gens civilisé, et que j'ai dit que j'acceptais toutes les critiques (ce qui signifie que j'étais prêt à être réactif), je vais proposer ce soir un autre exemple, réellement utile.
Il faut juste que je trouve une idée concrete, pas trop compliqué, et qui soit utile.
Si certain ont des idées d'ici ce soir ..., je suis tout ouïe !
yann_lo_san Messages postés 1137 Date d'inscription lundi 17 novembre 2003 Statut Membre Dernière intervention 23 janvier 2016 24
16 nov. 2006 à 15:49
L'exemple mis de coté, j'ai bien insisté sur le fait que ceci pouvait être utile ! Mais la question était es-ce vraiment utile en c++ ?
un delegate C# est juste un pointeur sur une fonction...
cs_Zeroc00l Messages postés 367 Date d'inscription lundi 1 avril 2002 Statut Membre Dernière intervention 11 février 2010
16 nov. 2006 à 23:30
Tu dis que cela peut etre utile... et apres tu te poses la question ?
On est pas en C# la !
Donc étant donné que c'est super interessant à utiliser dans les autres langages, c'est pour ca que je propose cela en C++.
De plus si tu regardes le fichier events.hh, ce sont egalement des pointeurs qui se cachent...

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.