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

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

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.