IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

[Conception] Observer/ envoyer une commande à un objet


Sujet :

C++

  1. #1
    Membre éclairé Avatar de seeme
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    430
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 430
    Points : 791
    Points
    791
    Par défaut [Conception] Observer/ envoyer une commande à un objet
    Bonjour,

    Pour mon projet, j'ai implémenté une console. Elle appel des fonctions qui sont préalablement enregistrées dans une map.

    Mon problème est le suivant: la console va avoir un impact non négligeable sur pas mal de classe.

    Si dans un moteur d'affichage, on a une commande du genre

    La console va trouver la fonction 'move' et l'appeler avec ses paramètres. Grâce au pattern Observer, je peux dire à mon cube qu'une commande le concernant a été émise.

    Problème, comment lui passer les paramètres correspondants?

    Dans le pattern, on appel une fonction notify(), mais comme je vais avoir des commandes avec ou sans paramètres, comment faire?

    Une solution que je vois serait de transformer notify en notify(const std::vector<std::strings>& args) avec en premier champ dans le vecteur le nom de la commande, suivit des arguments.

    Il faut ensuite trouver dans une map la fonction qui va bien et l'appeller..

    En gros c'est le mécanisme de la console, mais je dois le refaire pour chaque objet qui observe la console...

    Bref, je coince..
    Si vous avez des idées..

  2. #2
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Bonsoir,

    J'ai du mal à voir en quoi le patron "observer" rend service dans ce cas. Il rendrait à mon sens service pour implémenter "onmove cube1 ..." où le cube signalerait des changements. Là, il me semble que tu inverses le problème d'où le fait que le cube ne connaisse pas les arguments à passer à la commande.

    Pour le cas présent, tu peux peut être t'inspirer du patron "Command", voir du patron "Interpreter" si tu envisages des expressions complexes masquant un arbre d'expression.


    Bonne continuation

  3. #3
    Membre éclairé Avatar de seeme
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    430
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 430
    Points : 791
    Points
    791
    Par défaut
    Bonjour,

    D'abord bretus, merci pour ta réponse.

    En fait, mon problème ne se situe pas tant dans la lecture/interprétation/validation de la commande ou de ses arguments.

    Je vais essayer de réexpliquer.

    J'ai une classe A qui représente disons un cube dans l'espace:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    struct cube{
        vector coordonées;
        int taille;
    };
    à côté de ça, j'ai ma console qui lit des commandes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    struct console{
        parseCommande(commande, arguments);
    };
    Donc ma console a lu une commande. Maintenant, je veux que dans le cas où la commande serait "move cube1 x 10", que la valeur x du cube passe à 10.

    Les solution que je vois:
    • Pointeur sur le cube: on encapsule un pointeur vers le cube dans la console. Complètement inenvisageable, le couplage est beaucoup trop fort (mon appli est un moteur 3D, je peux pas me permettre d'avoir un pointeur vers le gestionnaire mémoire, un vers le graph de rendu, un vers le graph logique....)
    • Enregistrement d'un callback: l'objet Cube déclare une fonction
      auprès de la console. Le couplage me parait moins fort. C'est pas idéal, mais c'est faisable.
      Sauf que quand je fait mon tableau[commande/pointeur sur fonction] ça ne fonctionne pas, vu que le type de la fonction va être différent selon quelle est la classe qui enregistre (je pourrais enregistrer que des fonctions static, mais les attributs qu'elle pourront manipuler devront être statique aussi, pas possible...).
      J'ai regardé un peu boost::function/bind. Le problème c'est que ça me rajoute une dépendance à boost (j'en ai déjà une très légère et qui va bientôt sauter à boost::string algorithm).
    • Mettre en place un observer: comme tu l'as dis, ce n'est pas adapté. Si un objet peut être relié à plusieurs commandes (move, rotate...), je dois passer le nom de la commande dans le notify et me recogner un filtrage pour trouver la commande invoquée...



    Voilà où j'en suis... ça fait 2 jours que je coince dessus.. Si vous aviez des conseils à me donner...

    question subsidiaire: que reproche-t-on à la dépendance à boost? La taille, la lenteur, les deux? Là j'ai une dépendance à boost thread et une pour tokenize une chaine qui vont sauter bientôt, mais j'en ai surtout une grosse à boost:serialize... C'est grave docteur?

  4. #4
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Bonjour,

    Citation Envoyé par seeme Voir le message
    • Pointeur sur le cube: on encapsule un pointeur vers le cube dans la console. Complètement inenvisageable, le couplage est beaucoup trop fort (mon appli est un moteur 3D, je peux pas me permettre d'avoir un pointeur vers le gestionnaire mémoire, un vers le graph de rendu, un vers le graph logique....)
    Et pourtant, pour que la "console" puisse agir sur le cube, il faut bien qu'elle puisse récupérer l'instance du cube par son nom.

    En utilisant boost::bind, tu ne donneras pas accès pour autant au cube par son nom à la console. Pour exécuter la fonction "binder" (quelle horreur ) tu devras lui passer en paramètre un pointeur ou une référence sur le cube.

    Alors, comment donner accès aux objets par leurs noms à la console?

    En revenant sur l'idée du patron "Command", au lieu d'encapsuler un pointeur sur le cube dans la "console", tu peux peut-être passer à la console ton gestionnaire de mémoire à la console et ajouter une fonction sur le gestionnaire de mémoire qui te permette de récupérer un objet par son nom?

    Concrètement, en supposant que tu as une classe "ScriptValue" représentant un argument de la ligne de commande (boost::variant par exemple) et que ObjectPool est ton gestionnaire de mémoire, ca donnerait quelque chose du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    class Object3d {
    public:
     
    	virtual ~Object3d();
     
    	// renvoie le nom de l'objet ( "cube1" )
    	std::string const& getName() const ; 
     
    	// execute une commande avec un vecteur d'argument	
    	virtual ScriptValue execute( std::string const& command, std::vector< ScriptValue > const& args ); 
    };
     
     
    class Console {
    public:
     
    	void executeCommandLine( std::string const& commandLine )
    	{
    		std::string command;
    		std::string target;
    		std::vector< ScriptValue > args;
    		parseCommandLine( commandLine, command, target, args );
    		getObjectPool().getObjectByName(target).execute( command, args );
    	}
     
    	//...
    };
    Et enfin pour le cube :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
     
    class Cube : public Object3d {
     
    //...
    	virtual ScriptValue execute( std::string const& command, std::vector< ScriptValue > const& args )
    	{
    		if ( command == "move" ){
    			executeMove( args );
    		}else if ( command == "otherAction" ){
    			executeOtherAction( args );
    		}else{
    			_throwExceptionActionNotDefined( command, args );
    		}	
    	}
     
    	void executeMove( std::vector< ScriptValue > const& args ){
    		if ( args.size() % 2 != 0 ){
    			_throwExceptionBadArguments( command, args );
    		}
    	}
     
    };
    (C'est le "routage" dans execute qui peut être évité grâce à boost::bind qui permet de mapper les noms d'actions à des fonctions sur cube. Tu dois aussi pouvoir utiliser boost::bind pour éviter la méthode "execute" et gérer la liste des actions au niveau de la console, ce que j'aurais tendance à déconseiller pour des questions de ré-utilisation des mécanismes de script)


    J'espère que ça t'ouvrira des pistes

    ++

    PS (hors sujet) :

    - Mettre en place un interpréteur même basique représente vite un boulot assez énorme. Le gros du travail ne réside pas tant dans la mise en place des mécanismes de base, mais dans l'écriture des méthodes du type "move". Contrôle des arguments (on a vite envie d'une signature), conversion des arguments (on a vite envie de conversion implicite), routage des actions (on en a vite assez de les enregistrer et d'oublier de les enregistrer), messages d'erreurs etc...

    - Je ne connais pas tes contraintes, mais j'aurais tendance te conseiller d'exposer tes classes dans un moteur de script existant du style Python ou ECMA script. Ca rend plus de service dans la mesure où les fonctions de bases (mathématiques, manipulation de fichiers, manipulation de chaîne de caractères) sont déjà présentes et c'est paradoxalement moins compliqué à mettre en place. Il existe de nombreux outils pour ça :
    • SWIG (Python, java, php, perl, etc...) : Simple copie de headers et ajout d'infos avec certaines classes de la STL déjà exposée...
    • boost::python (je n'ai pas testé)
    • ...


    Sinon un qui me fait rêver avec en prime son débogueur QtScript :
    http://qt.developpez.com/doc/4.6/QScriptEngine/

  5. #5
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Salut

    Cette discussion est très intéressante, et j'ai déjà eu à faire à ce genre de problématique.

    Citation Envoyé par bretus Voir le message
    ajouter une fonction sur le gestionnaire de mémoire qui te permette de récupérer un objet par son nom?
    C'est exactement ce qu'il faut faire selon moi.

    Ensuite et bien... facile. Soit move est une opération que supportent tous les objets situés dans l'espace (ce que je souhaite pour le bien fondé de ta conception) dans ce cas il suffit à l'interpréteur d'appeler la bonne méthode sur le pointeur récupéré.

    Sinon, tu peux utiliser le pattern visiteur. Ca nécessite au minimum une méthode commune à tous les objets, mais une seule suffit, ça peut te faire gagner du temps en terme d'implémentation. Veux tu un exemple ?
    Find me on github

Discussions similaires

  1. Envoyer une commande à un processus déjà lancé
    Par Life Hunter dans le forum Shell et commandes GNU
    Réponses: 15
    Dernier message: 10/04/2007, 13h09
  2. Envoyer une commande Telnet (bibliothèque Jakarta)
    Par XKCorp dans le forum Entrée/Sortie
    Réponses: 1
    Dernier message: 19/09/2006, 13h57
  3. Envoyer une commande periodiquement dans l'applet
    Par Battosaiii dans le forum Applets
    Réponses: 2
    Dernier message: 11/12/2005, 16h57
  4. Comment envoyer une commande en HTTP ?
    Par Promeneur dans le forum Web & réseau
    Réponses: 5
    Dernier message: 08/06/2005, 17h45
  5. Comment envoyer une commande à une console ?
    Par seb_asm dans le forum Assembleur
    Réponses: 3
    Dernier message: 27/03/2004, 14h09

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo