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 :

Problème de l'héritage d'une fonction template


Sujet :

Langage C++

  1. #1
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 288
    Billets dans le blog
    2
    Par défaut Problème de l'héritage d'une fonction template
    Bonjour,

    je cherche un moyen de contourner l'impossibilité d'hériter d'un fonction membre template, quelqu'un a une idée?

    Un peu de détails. Je voudrais faire ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct Father
    {
       template <typename T>
       virtual void bar( const T & param ) = 0;
    };
     
    struct Son: public Father
    {
       template <typename T>
       void bar( const T & param ) { param.DoSomething(); }
    };
    Evidemment ça ne compile pas parce qu'une fonction membre template ne peut pas être virtuelle (encore moins virtuelle pure donc).

    Connaissez-vous des astuces pour contourner cette limitation?

  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 : 34
    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
    Bonjour,

    Pour imaginer une solution de contournement, il faudrait que tu dises ce que tu veux faire avec ces deux (ou plus classe) ? Dans quel contexte te sert cet héritage, pourquoi y-a-t'il un besoin de template dans ce contexte ?

  3. #3
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 288
    Billets dans le blog
    2
    Par défaut
    En fait, c'est plus un problème théorique, général.
    Je suis tombé sur ce problème concrètement dans un programme que sur lequel je travaille, et j'ai trouvé une solution simple (j'ai juste pas déclaré les fonctions dans la classe de base) et qui convient pour les besoins précis de cette appli. Mais je réfléchissais à une solution plus générique.
    Déjà, je ne comprend pas le pourquoi de cette limitation. Qu'est-ce qui empêche le compilateur de mettre une fonction template dans la vtable?

  4. #4
    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 : 34
    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
    Il faut bien voir qu'une fonction template n'a aucune réalité une fois la compilation passée, ce sont ces instanciations qui en ont une. D'où la problématique, je mets quoi comme instanciation dans la table ? La compilation se fait unité par unité puis ensuite l'ensemble est lié. Chaque unité pourrait déterminer ce qu'il a besoin de mettre dans la table, mais rien ne garantirait à l'éditeur de lien que le contenu de la table serait identique entre chaque unité.

  5. #5
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 288
    Billets dans le blog
    2
    Par défaut
    Je ne comprends pas. Une fonction template n'a, qu'elle soit virtuelle ou non, de réalité qu'à l'instanciation. Je ne comprend donc pas ce que ça change qu'elle soit virtuelle

  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 : 34
    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
    On va faire simple, disons que j'ai cette classe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    struct A
    {
      template<class>
      virtual void foo(){}
    };
    Maintenant j'ai deux fichiers :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    //B.cpp
    void bar(A& a)
    { a.foo<char>(); }
     
    //C.cpp
    void goo()
    { 
      A a;
      a.foo<int>();
      bar(a);
    }
    Après compilation, j'ai un truc (juste dans l'idée) 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
     
    //B.obj
    foo_char(a)
    { a.table[0](a); }
     
    bar(a)
    { foo_char(a); }
     
    //C.obj
    foo_int(a)
    { a.table[0](a); }
     
    goo()
    {
      a; a.table[0] = foo_int;
      foo_int(a);
      bar(a);
    }
    L'éditeur de liens met tout ensemble, pas de problème, sauf qu'on se retrouve à appeler deux fois foo<int>à la place de foo<int> puis foo<char> dans goo(). Et le compilateur ne peut pas faire autrement, il n'y a qu'une instance dans chaque fichier, donc il considère une table a un seul élément.

  7. #7
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Pour le problème initial, je soupçonne fort que T soit en réalité un variant, non ? Je doute que la fonction accepte n’importe quoi comme T.

    Cela dit, je pense qu’une bonne solution pour ça, c’est le pattern NVI, avec un truc 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
    29
    30
     
    class Father {
    protected:
       virtual void bar_(LightTWrapperBase& wrapper)=0;
    public:
       template<typename T>
       void bar(T const& param) {
           LightWrapper<T> p(param);
           bar_(p);
       }
    };
     
    class Son : public Father {
    protected:
       void bar_(LightTWrapperBase& wrapper) override;
    };
     
    // et
    class LightTWrapperBase {// mettre ici les méthodes utiles en virtuelles pures
    };
     
    template<typename T>
    class LightTWrapper {
    protected:
    T const& value;
    public:
    ...  // override de la classe de base
    };
    Note : ça coûte un appel virtuel en plus, et grossit probablement un peu l’exécutable.

  8. #8
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 288
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Pour le problème initial, je soupçonne fort que T soit en réalité un variant, non ? Je doute que la fonction accepte n’importe quoi comme T.
    Non non, dans mon code T est un type "classique", une structure. Et j'ai testé avec des types natifs, le résultat est le même.

  9. #9
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par r0d Voir le message
    Non non, dans mon code T est un type "classique", une structure. Et j'ai testé avec des types natifs, le résultat est le même.
    Ce que je voulais dire par là, c’est que tu as probablement des contraintes sur ton T, et donc, que tu peux extraire une interface (un concept) de ces contraintes.

    Partant de là, tu peux utiliser un polymorphisme sur ton paramètre à la place de la généricité, soit directement sur tes types, soit au moyen d’un wrapper léger comme je l’ai fait dans mon exemple.

  10. #10
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 288
    Billets dans le blog
    2
    Par défaut
    Ptin ça me soule j'y comprend rien aux templates c'est énervant. Pendant près de 10 ans, j'ai fais du c++ sans rien savoir sur les templates, et sans m'y intéresser (si si c'est possible), et j'en paie les frais maintenant... Vous avez pas un bon livre à me conseiller?

  11. #11
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Modern c++ design.

    Je pense aussi que pour bien faire, il te faudrait un bouquin de compilation précisant comment on implémente des templates façon c++, mais là, je ne sais pas quoi te conseiller (même pas sûr que ça existe sous forme de bouquin en fait).

  12. #12
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 288
    Billets dans le blog
    2
    Par défaut
    "Modern c++ design" je le connais presque par cœur... En plus, il n'est plus vraiment à jour.
    Comme tu dis, il me faudrait quelque chose de plus "bas niveau".

  13. #13
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Dans ce cas, peut-être commencer par le Dragon Book (https://fr.wikipedia.org/wiki/Dragon_book).

    Je ne sais plus s’il y a une partie consacrée à la généricité (je te confirme ça dès que je remets la main dessus). Dans tous les cas, c’est un must-read si tu veux des infos sur le fonctionnement d’un compilateur.

  14. #14
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Une template, c'est précisément ce que dit son nom: un modèle de fonction, et surtout pas une fonction générique (à la java)
    Il y aura plusieurs fonctions différentes créées à la compilation.

    Ton problème est difficile parce qu'il est à l'intersection de plusieurs contraintes et optimisation.

    Tu imagines bien que le compilateur ne peut pas générer à l'avance toutes les possibilités de la template (puisqu'elles sont infinies).
    Du coup, il n'en génère aucune à l'avance.
    Elles sont créées au besoin, là où c'est nécessaire.

    Pour cette même raison, une template membre d'une classe n'est pas compilée avec la définition de la classe, mais plus tard, quand elle sera utilisé avec un type concret.

    D'autre part, la virtualité repose sur le fait que les fonctions virtuelles existes à la fin de la définition de la classe.

    C'est un problème de moment d'existence.

  15. #15
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    Juste une petite note: Ce n'est pas spécifiquement les fonctions templates qui ne peuvent pas être virtuelles, mais les fonctions "plus templates que leur classe".
    Ce qui permet à une classe template d'avoir des fonctions virtuelles:
    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
    template<class T>
    struct A
    {
    	virtual T* DoSomething(); //OK
    };
     
    struct B : public A<int>
    {
    	int* DoSomething();
    };
     
    template<class T>
    struct C : public A
    {
    	T* DoSomething();
    }
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  16. #16
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 288
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Pour le problème initial, je soupçonne fort que T soit en réalité un variant, non ? Je doute que la fonction accepte n’importe quoi comme T.

    Cela dit, je pense qu’une bonne solution pour ça, c’est le pattern NVI, avec un truc 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
    29
    30
     
    class Father {
    protected:
       virtual void bar_(LightTWrapperBase& wrapper)=0;
    public:
       template<typename T>
       void bar(T const& param) {
           LightWrapper<T> p(param);
           bar_(p);
       }
    };
     
    class Son : public Father {
    protected:
       void bar_(LightTWrapperBase& wrapper) override;
    };
     
    // et
    class LightTWrapperBase {// mettre ici les méthodes utiles en virtuelles pures
    };
     
    template<typename T>
    class LightTWrapper {
    protected:
    T const& value;
    public:
    ...  // override de la classe de base
    };
    Note : ça coûte un appel virtuel en plus, et grossit probablement un peu l’exécutable.
    Soit j'ai encore rien compris, soit ça ne marche pas. Comment tu déclares les fonctions dans LightTWrapperBase? Je ne vois pas comment les déclarer sans qu'elles soient templates.

  17. #17
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par r0d Voir le message
    Soit j'ai encore rien compris, soit ça ne marche pas. Comment tu déclares les fonctions dans LightTWrapperBase? Je ne vois pas comment les déclarer sans qu'elles soient templates.
    Le T& est un membre de ta classe. Donc il ne figure plus dans les paramètres.

    Je prends un exemple plus complet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    struct LightWrapperBase {
       virtual int sum() = 0;
    };
     
    template<typename T>
    struct LightWrapper : public LightWrapperBase {
        T const& ref;
        LightWrapper(T const& r_) : ref(r_) {}
        int sum() override { return ref.sum(); } 
    };
    Le seul soucis est si tu as besoin de renvoyer un T : là, ça ne marchera pas. Mais sinon, l’idée c’est :
    - isoler l’interface (le concept) minimale que doit fournir T
    - wrapper cette interface dans une classe de base
    - faire une classe générique dérivant de cette interface fournissant les services

    En fait, tu recrées une hiérarchie entre des objets qui n’en avaient pas, spécifique à ton besoin, pour utiliser un polymorphisme dynamique.

  18. #18
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 288
    Billets dans le blog
    2
    Par défaut
    Ha ok je vois. Malheureusement, je suis dans le cas où j'ai besoin de renvoyer la valeur à d'autres fonctions templates :/ (Je n'ai besoin que de ça en fait).

    La solution que j'ai implémenté hier fonctionne mal, car je ne peux plus bénéficier de la substitution du type de base.

    Je me demande en fait si je ne serais pas en train de faire fausse route. Mon problème est le suivant:
    J'ai un objet qui comporte quelques fonctions templates. Cet objet est une sorte de contrôleur de sérialisation: il prend des valeurs de types indéterminés (d'où les fonctions membres templates), effectue quelques vérifications et mise en forme, puis les envoie à la la lib de sérialization. Ça fonctionnait bien jusqu'à ce que mon chef (qui est tout sauf développeur) décide brutalement (et en contradiction avec le cahier des charges, mais ce sont les risques du métier) qu'il devrait être possible de sérializer de plusieurs façons, en fonction d'un argument de la command line (ce qui signifie que la décision se fait au runtime, pas à la compilation).
    Je suis donc parti sur un DP template method: un objet SerializerBase avec les fonctions de serialization en virtuelles pures, et des héritages selon le type de serialization souhaitée. Et là bim, c'est le drame: je ne peux pas surcharger mes fonctions membre template. Et ta méthode ne fonctionne pas parce qu'il faut que je renvoie les valeurs reçues par ces fonctions au niveau N-1 (les libs de sérialization).

    Ce qu'a dit Médinoc m'a donné une idée: peut-être qu'il faudrait plutôt que je "templatise" mon serializer plutôt que d'en faire une hiérarchie type "template method".

  19. #19
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Comment marche ta lib de désérialisation ? Tu sais à l’avance quel type d’objet tu es en train de lire, ou tu reçois des objets arbitraires dont tu détermines le type au runtime (j’ai plutôt l’impression que tu es dans le premier cas).

    Si c’est bien le premier cas, il n’y a que la désérialisation qui doit poser problème, et je pense qu’il y a une solution à base de double dispatch, 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
     
    struct DeserializerBase
    {
    ...
    template<typename T>
    T deserialize() { return this->deserialize_(LightWrapper(T)).as<T>);
    protected:
    virtual ResultBase& deserialize_(LightWrapperBase& wrap)=0;
    };
     
    struct DeserializerJson : public DeserializerBase
    {
        void ResultBase& deserialize_(LightWrapperBase& wrap) { return wrap.deserialize(this); }
        template<typename T>
        T deserialize() {  /* do the deserialization and return it */ ); 
    };
     
     
     
    template<typename T>
    struct LightWrapper : public LightWrapperBase
    {
       ResultBase& deserialize(DeserializerJson& deserializer) override { return ResultBase(deserializer.deserialize<T>(); }
       ResultBase& deserialize(DeserializerXml& deserializer) override { return ResultBase(deserializer.deserialize<T>(); }
    ...
    };
    (fait en 10 min selon la méthode de la rache, mais vois-tu l’idée ?)

Discussions similaires

  1. aidee :problème héritage d une fonction
    Par mitnick2006 dans le forum C++
    Réponses: 8
    Dernier message: 02/06/2007, 22h16
  2. Réponses: 2
    Dernier message: 29/03/2007, 12h02
  3. Problème pour la création d'une fonction
    Par jipé95 dans le forum C
    Réponses: 5
    Dernier message: 10/12/2006, 14h28
  4. problème avec l'appel d'une fonction
    Par mademoizel dans le forum ASP
    Réponses: 5
    Dernier message: 26/06/2006, 15h04
  5. Pointeur sur une fonction template
    Par Progs dans le forum Langage
    Réponses: 2
    Dernier message: 15/02/2006, 20h25

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