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 :

Polymorphisme et méthodes template: problème de conception


Sujet :

Langage C++

  1. #1
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut Polymorphisme et méthodes template: problème de conception
    Bonjour,

    J'ai un problème de conception au niveau des templates et du polymorphisme. Je vais essayer d'expliquer le plus simplement mon problème et mes tentatives de résolution.

    Tout d'abord j'ai une classe extractor :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <class Dumper>
    class extractor
    {
      Dumper dumper;
      char* buffer;
     
      public:
        extractor(char* buffer, Dumper dumper) : buffer(buffer), dumper(dumper){}
     
        template<typename raw_type>
        raw_type extract();
    };
    Jusque là, rien de bien compliqué. Du coup j'ai également ces deux classes de dumper :

    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
    struct no_dumper
    {
    	template<unsigned int N, typename raw_type>
    	void dump(std::string& tag, raw_type data[N]) {}
    };
     
    class my_dumper
    {	
      FILE* dump_file;
     
      public:
        my_dumper(FILE* dump) :
    	  dump_file(dump) {}
     
        template<unsigned int N, typename raw_type>
        void dump(std::string& tag, raw_type data[N]) 
        {
           //...
        }
    };
    Tout va bien sauf que le choix d'un dumper ou d'un autre dépend d'une variable booléenne.

    Dans ce cas zut, il faut faire hériter extractor d'une classe de base, pour pouvoir manipuler extractor<my_dumper> et extractor<no_dumper> d'une manière uniforme...

    Ce que je fais, donc je crée extractor_base, ça compile et évidemment je n'ai pas déclaré mes méthodes virtual donc ce sont celles de extractor_base qui sont appelées et rien ne se passe parce qu'elles sont vides...

    Si je les déclare virtual, et bien on ne peut pas car c'est des méthodes templates...

    Du coup j'ai pensé a déplacé la virtualité du côté de dumper qui hérite de dumper_base. C'est déjà plus logique mais on a fait que déplacer le problème...

    Pour le dumper, je ne peux pas envisager le polymorphisme statique vu qu'il faut un fichier donné à l'exécution.

    Par contre, on peut éventuellement choisir le type d'extractor statiquement en fonction d'un const boolean.

    Quelle est l'idiome à utiliser, ou quelle partie du design dois-je revoir ?

    Merci d'avance.

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Je n'ai pas pris le temps de me pencher dans le détail sur ton problème, mais connais-tu le CRTP ?

    Si ton booléen est connu de manière statique, jette également un œil au static if.

  3. #3
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Merci pour cette réponse, en effet le static if est une bonne idée et ça résout déjà un problème mais malheureusement pas tout.

    Pour ce qui est du CRTP, je connais mais je n'ai aucune idée de comment ça peut me servir ici.

    Avec le static if j'aurai par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    IF<have_dump, extractor<my_dumper>, extractor<no_dumper> >::type extractor(data, ??);
     
    // Si have_dump == true alors la construction 
     
    extractor(data, my_dumper(mon_fichier));
     
    // Sinon
     
    extractor(data, no_dumper());
    Comment faire alors ?

  4. #4
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Bon, je ne suis pas vraiment à l'aise avec ces trucs là, mais si tu utilises C++ 11 :

    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
    #include <type_traits>
    #include <typeinfo>
     
    class my_dumper
    {
        ....
    };
     
    class no_dumper
    {
        ....
    };
     
    int main() 
    {
        ....
     
        typedef std::conditional<have_dump, my_dumper, no_dumper>::type DumperT;
     
        DumperT dumper;
     
        char* data = ...
        extractor(data, dumper);
    }

  5. #5
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Merci de ta réponse, mais non je n'utilise pas C++11. Mais peu importe, j'ai pu faire un static_if facilement. Ce qui donne comme appel :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    typedef static_if<have_dumper, Extractor<my_dumper>, Extractor<no_dumper> >::type extractor_type;
     
    extractor_type extractor = extractor_type(buffer, extractor_type::dumper_type(dump_file));
    Le problème est que

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    extractor_type::dumper_type(dump_file)
    n'a pas nécessairement ce constructeur. no_dumper a un constructeur sans argument et l'autre a un constructeur avec un argument.

    J'ai temporairement réglé ce problème en mettant un constructeur à un argument inutile à no_dumper.

    Donc mon problème initial est résolu temporairement mais si vous avez d'autres propositions je suis preneur.

    Et d'ailleurs, est-ce que avec ma solution de mettre un paramètre inutile au constructeur de no_dumper, le compilateur sera toujours en mesure de comprendre que no_dumper ne sert à rien et qu'il ne faut pas appeler sa méthode vide ?

    Tant que j'y suis, je me demandais comment on aurait pu résoudre mon problème si le booléen "have_dumper" était choisis à l'exécution ?

    Merci pour vos réponses.

  6. #6
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par Trademark Voir le message
    Dans ce cas zut, il faut faire hériter extractor d'une classe de base, pour pouvoir manipuler extractor<my_dumper> et extractor<no_dumper> d'une manière uniforme...
    Si tu veux pouvoir avoir une utilisation uniforme, il te faut bien avoir une interface commune. Notamment pour le constructeur...

  7. #7
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Tant que j'y suis, je me demandais comment on aurait pu résoudre mon problème si le booléen "have_dumper" était choisis à l'exécution ?
    Si tu n'as que deux cas, ou même si tu as un nombre fini de cas connus par extractor, tu n'as pas besoin de virtuels. Il suffit de générer tous les cas et de choisir le bon.

    typedef static_if<have_dumper, Extractor<my_dumper>, Extractor<no_dumper> >::type extractor_type;
    Pourquoi ne pas utiliser les méta-fonctions prévues pour dans Boost ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef boost::mpl::if_c<have_dumper, Extractor<my_dumper>, Extractor<no_dumper> >::type extractor_type;
    Boost ftw

  8. #8
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Si tu n'as que deux cas, ou même si tu as un nombre fini de cas connus par extractor, tu n'as pas besoin de virtuels. Il suffit de générer tous les cas et de choisir le bon.
    Je ne suis pas sur de comprendre, tu veux dire faire quelque chose comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template <class Extractor>
    void extract_with(Extractor &my_extractor)
    { ... }
     
    if(have_dump)
    {
      extractor<my_dumper> my_extractor(...);
      extract_with(my_extractor);
    }
    else
    {
      extractor<no_dumper> my_extractor(..);
      extract_with(my_extractor);
    }
    Ou avais-tu en tête autre chose ?

    De plus, je chipote, mais on a un appel de fonction redondant ^^, si c'est de cette méthode dont tu parles, existe t'il un moyen de s'en passer ?

    Pourquoi ne pas utiliser les méta-fonctions prévues pour dans Boost ?
    Ha malheur, je ne peux même pas utiliser la STL alors Boost, c'est un doux rêve. C'est la réalité de l'entreprise dans laquelle je fais mon stage. Si des clients veulent compiler avec un compilateur de Mathusalem et bien il faut faire avec

    Déjà que je "transgresse" les règles en faisant de la métaprog vu que on est sensé produire du code que VC6 compile... Enfin, à priori, mon code n'aura pas besoin d'être compilé avec du VC6.. Donc je me le permet.

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,

    Tu pourrais aussi très bien "simplement" créer deux structures vides qui serviront de flag:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    struct dumper_flag{}; // il faut dumper
    struct nodumper_flag{};
    A partir de là, tu peux utiliser la spécialisation template pour ton dumper :
    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
     
    template <typename Flag>
    struct Dumper;
    template <>
    struct Dumper<nodumper_flag>
    {
    	template<unsigned int N, typename raw_type>
    	void dump(std::string& tag, raw_type data[N]) {}
    }
    struct Dumper;
    template <>
    struct Dumper<dumper_flag>
    {	
      FILE* dump_file;
     
      public:
        my_dumper(FILE* dump) :
    	  dump_file(dump) {}
     
        template<unsigned int N, typename raw_type>
        void dump(std::string& tag, raw_type data[N]) 
        {
           //...
        }
    };
    et, une fois que cela est fait, tu n'a plus qu'à créer ta classe template en passant non pas le nom du fichier, mais... le dumper :
    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
    template <typename Raw,  int, typename Helper>
    class Extractor
    {
      public:
        typedef Helper helper_type;
        typedef Raw raw_type;
        extractor(const char * buffer, helper_type const & helper) : buffer(buffer), helper_(helper){}
        raw_type extract()
        {
            helper_.dump(/* le tag */, /* le tableau de données */);
        }
        private:
            char *  buffer;
            helper_type helper_;
    };
    Au final, tout dépendra du dumper que tu auras utilisé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int main()
    {
        FILE * file = fopen("filename.txt");
        char * buffer;
        /* ...*/
        Dumper<nodump_flag> nodump; //  un dumper qui ne fait rien
        Dumper<dump_flag> dump(file); // un autre qui injecte les données dans le fichier
        Extractor extractToNull(buffer, nodump);
        extractToNull.extract(); // revient à faire Dumper<nodump_flag>::dump()
        Extractor extractReal(buffer, dump);
        extractReal.extract(); // revient à faire Dumper<dump_flag>::dump()
        return 0;
    }
    Et, si tu veux, tu peux même "cacher" le template de dumper derrière des typedef:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    typedef Dumper<nodump_flag> DumperToDevNull;
    typedef Dumper<dump_flag> DumperToFile;
    (tant qu'à utiliser les template, allons au bout de leur utilisation )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  10. #10
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Merci pour cette réponse, je vois comment on pourrait gérer facilement si le booléen était calculé au runtime maintenant

    Par contre, il n'a jamais été question de passer le nom du fichier directement à l'extracteur

    Merci encore à tous ceux qui m'ont répondu.

  11. #11
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Trademark Voir le message
    Merci pour cette réponse, je vois comment on pourrait gérer facilement si le booléen était calculé au runtime maintenant

    Par contre, il n'a jamais été question de passer le nom du fichier directement à l'extracteur

    Merci encore à tous ceux qui m'ont répondu.
    Au temps pour moi... je me suis laissé abusé par le char *

    Mais, l'idée, de toutes manières, c'est que ce soit le dumper qui dispose du fichier, si tant est qu'il en ait besoin, et, comme le dumper est fourni à l'extracteur, tout va bien
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 5
    Dernier message: 30/08/2010, 18h26
  2. Réponses: 19
    Dernier message: 23/12/2009, 19h22
  3. Réponses: 0
    Dernier message: 09/11/2008, 14h33
  4. problème méthode template
    Par CedricMocquillon dans le forum Langage
    Réponses: 12
    Dernier message: 08/08/2008, 17h31
  5. Méthode Finalize et problème de conception
    Par phryos dans le forum Langage
    Réponses: 4
    Dernier message: 19/04/2006, 11h04

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