begin process at 2012 02 13 02:19:56
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Astuces

 > GESTION (OU SIMULATION) DES ÉVENEMENTS EN C++ PURE

GESTION (OU SIMULATION) DES ÉVENEMENTS EN C++ PURE


 Information sur la source

Note :
8,75 / 10 - par 4 personnes
8,75 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Astuces Classé sous :gestion, evenement, simuler, callback, event Niveau :Initié Date de création :16/11/2006 Date de mise à jour :23/02/2010 10:06:49 Vu / téléchargé :9 961 / 380

Auteur : Zeroc00l

Ecrire un message privé
Ce membre participe au partage de revenus publicitaires
Commentaire sur cette source (13)
Ajouter un commentaire et/ou une note


 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

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

 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 Historique

16 novembre 2006 01:54:51 :
Reformatage du texte
16 novembre 2006 02:08:55 :
reformatage
23 février 2010 10:06:49 :
-- Ajout du mot clé "typename" un peu partout pour gérer les types templaté -- Renommage de certains arguments de macro => plus explicite. -- Ajout d'un autre exemple (celui visible sur cette page, l'autre étant dans le zip). -- Modification de la Macro SLOT afin qu'elle vérifie la signature de la fonction prise en argument. Ainsi si l'argument de la fonction n'est pas un pointeur, une erreur probablement assez explicite est déclenché. Cette vérification est statique et faite avec des templates. -- Ajout d'un constructeur par copie pour la classe interne __Event, qui ne copie pas les abonnements (on veut rarement copier les évènements). -- Tout le code a été déplacé dans le namespace "Event", cependant rien ne change pour le code utilisateur.

 Sources du même auteur

GENERATION DE L'EXPRESSION REGULIERE (REGEXP) POUR MANGER JU...

 Sources de la même categorie

Source avec Zip SCHEDULER RR FIFO par yvesB87
Source avec Zip ALGORITHMES RÉCURSIFS VS ALGORITHMES ITÉRATIFS par yvesB87
Source avec Zip Source avec une capture C++ FORMAT D'IMAGE AVEC QT par pop70
Source avec une capture EXEMPLE DE POINTEURS DE FONCTION par pop70
Source avec Zip Source avec une capture [C++] CLASS REGISTER par Miwik

 Sources en rapport avec celle ci

Source avec Zip Source avec une capture SHOP MANAGER CONSOLE SUR WINDOWS par antho974
Source avec Zip Source avec une capture GETIONNAIRE D'UNE BIBLIOTHÉQUE EN C par benzarabel
Source avec Zip Source avec une capture UN GESTIONNAIRE DU FICHIER par benzarabel
Source avec Zip Source avec une capture INFORMATION GTK CONCERNANT UNE TOUCHE par Phelim
Source avec Zip Source avec une capture GESTION DE BIBLIOTHEQUE par moujahid88

Commentaires et avis

Commentaire de Arnaud16022 le 16/11/2006 12:44:39

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 :'(

Commentaire de yann_lo_san le 16/11/2006 14:30:43

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

Commentaire de Zeroc00l le 16/11/2006 14:59:33

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 !

Commentaire de yann_lo_san le 16/11/2006 15:49:27

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

Commentaire de Zeroc00l le 16/11/2006 23:30:38

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

Commentaire de yann_lo_san le 17/11/2006 00:41:38

Heu, je retire ce que j'ai dit, Microsoft l'a implémenté en native c++ (managed je présume) :

// Vu dans MSDN

#include <stdio.h>

[event_source(native)]
class CSource {
public:
   __event void MyEvent(int nValue);
};

[event_receiver(native)]
class CReceiver {
public:
   void MyHandler1(int nValue) {
      printf("MyHandler1 was called with value %d.\n", nValue);
   }

   void MyHandler2(int nValue) {
      printf("MyHandler2 was called with value %d.\n", nValue);
   }

   void hookEvent(CSource* pSource) {
      __hook(&CSource::MyEvent, pSource, &CReceiver::MyHandler1);
      __hook(&CSource::MyEvent, pSource, &CReceiver::MyHandler2);
   }

   void unhookEvent(CSource* pSource) {
      __unhook(&CSource::MyEvent, pSource, &CReceiver::MyHandler1);
      __unhook(&CSource::MyEvent, pSource, &CReceiver::MyHandler2);
   }
};

void main() {
   CSource source;
   CReceiver receiver;

   receiver.hookEvent(&source);
   __raise source.MyEvent(123);
   receiver.unhookEvent(&source);
}

Commentaire de Zeroc00l le 17/11/2006 01:41:55

Merci pour cette remarque très constructive :) !
En revanche, sous linux, pas d'autre alternative à ma connaissance avec g++ :/ !

Commentaire de Zeroc00l le 17/11/2006 01:43:13

Et puis dans le titre j'ai dit en "C++ pure",
donc c'est sans compter sur les compilateurs qui ajoutent leurs extensions :)

Commentaire de yann_lo_san le 17/11/2006 13:45:14

Désolé pour le 6/10, si je pouvait renoter je mettrais plutot 8/10 (pour le .hh avec templates).
Bonne continuation.

Commentaire de racpp le 17/11/2006 16:31:27 administrateur CS

yann_lo_san >> J'ai supprimé le "6/10". Tu peux renoter.

Commentaire de badrbadr le 18/11/2006 03:23:50

Belle source, il reste maintenant juste à ajouter le support thread pour des événements non-bloquants :p

Commentaire de xydion le 16/02/2010 11:03:20

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

Commentaire de Zeroc00l le 23/02/2010 10:14:07

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

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

Simuler un evenement Directx (DirectInput) [ par goshiz ] bonjour,je souhaite faire croire a un Jeu utilisant DirecX qu'un bouton de la souris a été enfoncé.Comment faire ? Gestion des evenement windows [ par vinceVD ] Salut a tous.J'ai une question qui va vous paratire toute bête : Comment je peux faire pour géré les evenement dans mon programme principale.pour le keybd_event [ par Steph115 ] Bonjour,pour simuler une combinaison de touches (par exemple ALT+ENTRER) il faut utiliser cette fonction la :&nbsp;keybd_event(VK_MENU,0x38,0,0);&nbsp keybd_event [ par jereboss188 ] Bonjour à tous,Pour commencer, merci d'avance au personnes qui pourront (ou tenteront de) repondre à ma question.Je développe en C++ un programme pour simuler l'evenement clique souris [ par azamharir ] salut, Dans un projet SDI, je suis amené à simuler un click souris sur un composant (activeX). Cet activeX est MO21Legend.ocx d'ESRI. Dans mon CView simuler l'appui des touches F2 et F3 [ par weahgeorges ] Bonjour,mon probléme est le suivant. Je dois simuler l'appui de touches sur une application citrix. Pour cela j'utilise la fonction keybd_event. Lorsq Gestion des evenements d'une fenetre sans CallBack ???? [ par andrebernard ] Bonjour à tous J'ai une simple question à poser. En C, est il possible de gérer les évènements d'une fenêtre et de ses contrôles (TextBox, Bouton, et problème pour simuler touches [ par jeromedu94 ] bonjour,je voudrais simuler les touches windows et D pour aller sur le bureau, j'ai essayer &#231;a mais &#231;a ne marche pas et je ne vois pas pourq Simuler mon clavier a partir de mon site web [ par supergrey ] Salut, je voudrais savoir comment faire pour simuler des appuis sur les touches de mon clavier a partir d'une page html sur&nbsp;mon site web.Je sais génere un evenement vb à partir de vc++ [ par amiro ] j'utilise une callback function dans une DLL.comment peut on faire pour g&#233;n&#233;rer un &#233;v&#233;nement visual basic lorsque cette callback f


Nos sponsors


Sondage...

CalendriCode

Février 2012
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
272829    

Consulter la suite du CalendriCode

Photothèque

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,562 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales