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

Langage C++ Discussion :

Visiteur dynamique basé sur le RTTI


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre expérimenté Avatar de vikki
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    292
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mai 2007
    Messages : 292
    Par défaut Visiteur dynamique basé sur le RTTI
    Bonjour à tous,
    Dans un gros projet que je développe actuellement, j'utilise en masse le pattern visiteur pour étendre les fonctionnalités de mes hiérarchies classes. Outre le coté laborieux de la chose lorsque je rajoute une classe dans ma hiérarchie (modifier tous les visiteurs dépendant de la classe de base), je dois maintenant implémenter un système de plugin permettant d'ajouter dynamiquement de nouvelles classes. Et donc impossible d'étendre les fonctionnalités de ces classes avec mes visiteurs classiques.

    Je cherche donc une solution de remplacement et je suis tombé sur cette article décrivant un visiteur dynamique basé sur le RTTI. Le principe et d'enregistrer dynamiquement les fonctions de visitation (si ca se dit) pour chaque type. J'ai implémenté une première version de ce visiteur et je trouve ca plutôt pratique à utiliser, je peux même l'utiliser pour étendre mes précédents visiteurs (par héritage) sans foutre en l'air tout mon code.
    J'aimerais avoir votre avis sur ce visiteur dynamique et ces éventuelles limitations (utilisation des fonctions de RTTI, faible vitesse d'exécution, etc...), voir s'il est totalement à proscrire.

    Pour vous donner une idée plus précise, je poste également une première implémentation de ce visiteur dynamique.

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
     
    #include <boost/function.hpp>
    #include <boost/bind.hpp>
    #include <map>
    #include <iostream>
    #include <typeinfo.h>
     
    //conversion du parametre de type BASE_CLASS_TYPE vers CLASS_TYPE
    template<class CLASS_TYPE, class BASE_CLASS_TYPE, class VISITOR_TYPE>
    void call(void (*f)( VISITOR_TYPE &, CLASS_TYPE & ),  VISITOR_TYPE & visitor, BASE_CLASS_TYPE & obj)
    {
            f(visitor, static_cast<CLASS_TYPE&>(obj));
    }
     
    //modification du prototype de la fonction de visitation de void(VISITOR_TYPE&, BASE_CLASS_TYPE&) vers void(VISITOR_TYPE&, CLASS_TYPE&)
    template<class CLASS_TYPE, class BASE_CLASS_TYPE, class VISITOR_TYPE>
    boost::function<void(VISITOR_TYPE &, BASE_CLASS_TYPE &)> bind_function(void (*f)(VISITOR_TYPE &, CLASS_TYPE & ))
    {
        return boost::function<void(VISITOR_TYPE &, BASE_CLASS_TYPE &)>( boost::bind( call<CLASS_TYPE,BASE_CLASS_TYPE,VISITOR_TYPE>, f, _1 , _2) );
    }
     
    //map static de const type_info* vers boost::function<void(VISITOR_TYPE &,BASE_CLASS_TYPE &)
    template< class VISITOR_TYPE, class BASE_CLASS_TYPE>
    static std::map<const type_info*,boost::function<void(VISITOR_TYPE &,BASE_CLASS_TYPE &)>> & functions()
    {
        static std::map<const type_info*,boost::function<void(VISITOR_TYPE &,BASE_CLASS_TYPE &)>> static_map;
        return static_map;
    }
     
    //Le visiteur de base
    template<class VISITOR_TYPE, class BASE_CLASS_TYPE>
    class BaseVisitor
    {
    public:
     
        typedef boost::function<void(VISITOR_TYPE &,BASE_CLASS_TYPE &)> function_type;
     
        //enregistrement d'une fonction de visitation
        template< class CLASS_TYPE >
        static void Register(void (*fn)(VISITOR_TYPE & ,CLASS_TYPE & ) )
        {
            functions<VISITOR_TYPE,BASE_CLASS_TYPE>()[&typeid(CLASS_TYPE)] = bind_function<CLASS_TYPE,BASE_CLASS_TYPE,VISITOR_TYPE>(fn);
        }
     
        //renvoie la fonction de visitation, pas indispensable
        template< class CLASS_TYPE >
        static function_type GetFunction()
        {
            std::map<const type_info*,function_type>::iterator it = functions<VISITOR_TYPE,BASE_CLASS_TYPE>().find(&typeid(CLASS_TYPE));
            if(it != functions<VISITOR_TYPE,BASE_CLASS_TYPE>().end())
                return it->second;
            else
                return function_type();
        }
     
        //Visite un objet
        virtual void Visit(BASE_CLASS_TYPE & obj)
        {
            std::map<const type_info*,function_type>::iterator it = functions<VISITOR_TYPE,BASE_CLASS_TYPE>().find(&typeid(obj));
            if(it != functions<VISITOR_TYPE,BASE_CLASS_TYPE>().end())
                it->second(static_cast<VISITOR_TYPE&>(*this),obj);
            else
                DefaultBehavior(obj);
        }
     
        //Comportement par default pour les classes qui ne sont pas enregistrées
        virtual void DefaultBehavior(BASE_CLASS_TYPE & )
        {}
    };
     
     
     
    //Hiérarchie de classe bidon, avec fonction virtuelle obligatoire pour l'utilisation du RTTI
    struct A{
        int virtual get(){return 0;}
    };
     
    struct B : public A{
     
    };
     
    struct C : public A{};
     
     
    //Un visiteur quelconque
    class visitor : public BaseVisitor<visitor,A>
    {};
     
    //Les fonctions de visitation
    void testA(visitor & v, A & a)
    {
        std::cout<<"A"<<std::endl;
    }
     
    void testB(visitor & v, B & b)
    {
        std::cout<<"B"<<std::endl;
    }
     
    void testC(visitor & v, C & b)
    {
        std::cout<<"C"<<std::endl;
    }
     
     
    int main()
    {
        A *a=new A;
        A *b=new B;
        C *c=new C;
     
        //enregistrement des fonctions
        visitor::Register(&testA);
        visitor::Register(&testB);
     
        visitor v;
        v.Visit(*a);
        v.Visit(*b);
        v.Visit(*c);
     
        std::system("pause");
    }

  2. #2
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    J'ai pas encore pris le temps de regarder ton code ni l'article.

    Qu'entend tu par "ajouter dynamiquement une classe" ? Je vois pas comment tu pourrais ajouter une classe à l'execution d'un programme, ca me semble vraiment impossible.

    Pour ce qui est d'utiliser le RTTI pour un dispatcher, effectivement c'est le plus efficase, mais c'est aussi le plus intruisif.

    Je te conseil de regarder comment Loki implémente ce pattern, il propose divers stratégié (RTTI notamenr).

  3. #3
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    salut,
    Pareil que flob, je n'ai pas pris le temps de lire l'article et le code Mais je me souvient d'un billet d'Emanuel Deloget intéressant sur le visiteur : V comme visiteur

  4. #4
    Membre expérimenté Avatar de vikki
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    292
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mai 2007
    Messages : 292
    Par défaut
    Hello, merci pour ta réponse!

    Qu'entend tu par "ajouter dynamiquement une classe" ? Je vois pas comment tu pourrais ajouter une classe à l'execution d'un programme, ca me semble vraiment impossible.
    Je me suis mal exprimé. Il s'agit de définir une classe B dans une librairie dynamique héritant d'une classe A connue par le programme principale. La libraire fournie simplement une fonction permettant de récupérer un A* pointant sur une instance de B. La classe B n'est pas connue par le programme et les visiteurs n'y ont donc pas accès.
    Pour ce qui est d'utiliser le RTTI pour le visiteur, effectivement c'est le plus efficace, mais c'est aussi le plus intrusif.
    Le visiteur auquel je pense est (je crois) un des moins intrusif possible. Il ne nécessite pas que les classes visitées possèdent une fonction Accept(visitor &) (elles doivent cependant posséder une fonction virtuelle, ne serait-ce que par héritage, pour faire fonctionner le RTTI).

    Je te conseil de regarder comment Loki implémente ce pattern, il propose divers stratégié (RTTI, cyclic, acyclic, ...), et il me semble que ca represente un peu près tout les visiteurs possible (execption faite pour ceux devant gérer des hiérarchie profonde d'une certaine manière)
    Je viens de regarder brièvement loki, et il semble que, quel que soit le visiteur, tous les types visitables doivent être connus à la déclaration du visiteur, donc à la compilation (à vérifier).

  5. #5
    Membre expérimenté Avatar de vikki
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    292
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mai 2007
    Messages : 292
    Par défaut
    Pareil que flob, je n'ai pas pris le temps de lire l'article et le code Mais je me souvient d'un billet d'Emanuel Deloget intéressant sur le visiteur : V comme visiteur
    J'étais effectivement tombé sur ce billet en cherchant une solution, mais le visiteur présenté utilisant le RTTI impose également la connaissance de tous les types visitables à la compilation. D'où la prise de tête avec ce système de plugins

    EDIT: pas besoin de lire tout l'article en lien, seul la dernière partie (intitulée Indirect Visitor) concerne la description de ce visiteur dynamique (heu... visiteur indirect en fait, j'aurais peut être du mettre ce mot dans l'intitulé du post)

  6. #6
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Tu as regarder la partie dispatch de Loki ? Car c'est en réalité ce que tu fais.

    J'ai lu en diagonal ton article, et c'est bien dans l'optique d'un dispatcher qu'il propose ceci, pas vraiment d'un simple visiteur.

    Le dispatcher de Loki qui te concerne est le <edit> BasicDispatcher </edit>

    (Je vais étider mon premier message, une partie porte à confusion).

  7. #7
    Membre expérimenté Avatar de vikki
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    292
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mai 2007
    Messages : 292
    Par défaut
    Effectivement, le BasicDispatcher de Loki ressemble beaucoup à ce que je souhaite faire. Je vais regarder ca plus en profondeur, merci pour l'info!

  8. #8
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par vikki Voir le message
    J'étais effectivement tombé sur ce billet en cherchant une solution, mais le visiteur présenté utilisant le RTTI impose également la connaissance de tous les types visitables à la compilation. D'où la prise de tête avec ce système de plugins
    Je crois qu'il y a confusion là. Un visiteur dans une application ne peut pas savoir comment visiter un objet défini dans un plugin. C'est compréhensible, et relativement logique. L'idée d'un système de plugin, c'est de permettre l'extension des fonctionnalités du programme hôte, par de les prévoir toutes.

    Par contre, un visiteur dans un plugin peur parcourir des éléments définis dans l'application. La, c'est logique.

    Finalement, un visiteur dans un plugin peut visiter un objet de ce plugin.

    Le visiteur acyclique présenté dans mon article, de même que celui présenté dans l'article cité par le PO :
    * s'il est défini dans un plugin, il peut visiter les types de l'application ; il lui suffit de dériver des classes définies dans l'application.
    * s'il est défini dans l'application, il ne peut pas visiter les types visitables définis dans le plugin.
    * en même temps, si le type visitable est défini dans le plugin, alors son visiteur l'est aussi - sinon ça n'a guère d'intérêt.

    Je ne vois pas où est le problème, en fait.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  9. #9
    Membre expérimenté Avatar de vikki
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    292
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mai 2007
    Messages : 292
    Par défaut
    Je m'étais mal exprimé au début, ce n'est pas vraiment un visiteur que je cherche mais un dispatcher. Et ce dispatcher doit être capable de s'appliquer sur une classe définie dans un plugin via une fonction que l'on enregistre dans le dispatcher. Si je prends un exemple un peu simpliste:

    L'application définie une classe de base A et quelques classes dérivées, ainsi qu'un dispatcher permettant de modifier des éléments d'interface graphique en fonction du type de la classe appelée (un peu comme un visiteur). Le plugin suivant défini une classe B ainsi qu'un nouveau comportement lorsque le dispatcher prend en argument un objet de type B:

    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
    #include "Classe_A.h"
    #include "GUI_Dispatcher.h"
     
    class B: public A
    {
    // do some stuff
    };
     
    //fonction definissant le comportement du dispatcher pour la classe B
    void modify_GUI_B( B & b)
    {
       //modification de l'interface graphique
    }
     
    //fonction d'initialisation du plugin
    void initPlugin()
    {
         //on enregistre modify_GUI_B dans le dispatcher
         GUI_Dispatcher::Register(&modify_GUI_B);
    }
    Dans l'application je peux faire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    A * a = une_Instance_Quelconque
    GUI_Dispatcher dispatcher;
    dispatcher.dispatch(*a);
    Bien sur il s'agit ici de simple dispatch dont pas grand intérêt mais c'est le même principe pour du double (ou plus) dispatch. Loki propose d'ailleurs ce type de double dispatcher.

Discussions similaires

  1. [MySQL] Menu déroulant dynamique basé sur mysql
    Par Stéph utilisateur d'acces dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 21/07/2010, 18h18
  2. [2.2.2] Datacube basé sur dataset dynamique
    Par elbrujo2323 dans le forum BIRT
    Réponses: 0
    Dernier message: 08/10/2009, 11h20
  3. Instance dynamique basé sur nom de variable
    Par Chromozome dans le forum C#
    Réponses: 3
    Dernier message: 15/02/2009, 20h39
  4. Menu dynamique basé sur une table
    Par Anto03 dans le forum ASP.NET
    Réponses: 5
    Dernier message: 09/07/2008, 12h24
  5. Réponses: 2
    Dernier message: 07/10/2007, 16h59

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