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 :

Template dans classe abstraite


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Par défaut Template dans classe abstraite
    Bonjour.

    Je réalise un programme de simulation avec Qt et je suis en train de concevoir une classe "AbstractComputation" destinée à réaliser l'interface entre Qt et les simulations. Ainsi, l'interface Qt interagira toujours avec des classes dérivées de AbstractComputation, tandis que les développeurs de simulation pourront faire ce qu'ils veulent comme simulation en dérivant leurs classes de AbstractComputation.

    Le problème est que je cherche à faire deux fonctions setData(QString identifier, T data) et getData(QString identifier) permettant de passer des données (par exemple les conditions initiales) entre Qt et entre les classes de simulation dérivées de AbstractComputation. Le mécanisme auquel j'ai pensé (mais qui n'est peut être pas le bon), est d'avoir ces deux fonctions
    setData et getData qui affectent le paramètre identifié par la "QString identifier" au bon datamember de la classe dérivée correspondant.

    1) Question 1 : est-ce la bonne façon de faire ?

    Mon problème est que ça m'arrangerai de faire pouvoir passer n'importe quel type de données, et donc d'utiliser un template. Or ce sont avec les classes dérivées de AbstractComputation que les setData et getData seront utilisés.

    2) Question 2 : du coup cela risque-t-il de poser problème si getData<double> n'a pas été instancié au niveau de AbstractComputation mais l'a été via une classe dérivée ?

    Dites le moi si tout cela n'était pas très clair, que j'essaye de reformuler ça.

    Merci beaucoup

  2. #2
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Bonjour.

    Les templates sont justement là pour les cas génériques.
    S'il n'y a pas de spécialisation ou de redéfinition pour un paramètre donné, c'est la version générique de la fonction qui est exécutée.
    À condition qu'elle est été définie...

    Par contre, templates et héritage ne font pas bon ménage.
    Enfin, disons templates et virtualité.
    Une fonction template ne peut pas être virtuelle (je ne saurais pas t'expliquer clairement pourquoi).


    De plus, en C++ on ne peut pas accéder à une variable membre par son nom via une chaîne de caractères.
    Sauf si pour représenter les variables membre on utilise un tableau associatif...

    Alors je crains fort qu'il te faille trouver un autre moyen...


    Ah bah justement, une façon de faire est de placer un tableau associatif en tant que membre (privé ou protégé ) de AbstractComputation, les clés étant des chaînes de caractères, les valeurs des pointeurs vers un type servant de classe de base pour tous les types susceptibles d'être en donnée.
    Du coup, il faudra réencapsuler les types de base...

    Ce sont les classes dérivées qui rempliront ce tableau, mais on pourra accéder à toutes les « propriétés » à partir de AbstractComputation.
    Mais bon, ça reste une idée comme ça, pas sûr que ce soit la meilleure...


    Sinon, j'ai l'impression qu'il y a un problème entre le nom de ta classe abstraite et le rôle que tu veux lui attribuer.
    Une simulation devrait pouvoir s'affranchir de toute représentation graphique, ne serait-ce que pour avoir plusieurs vues différentes d'une même simulation, ou pour être indépendante de toute bibliothèque graphique.
    De l'autre côté, une interface devrait se contenter de transférer des données d'un objet à un autre, quitte à les transformer dans un format compréhensible par le second.

    Donc une interface n'a pas à effectuer de simulation, et une simulation n'a pas à s'occuper de l'interfaçage avec la bibliothèque graphique.
    Alors soit le nom que tu as choisi n'est pas approprié, son tu veux lui octroyer trop de rôles.
    Une classe ne devrait avoir qu'une seule responsabilité...


    Je te conseille de réfléchir encore à ton modèle d'interfaçage.
    Peut-être qu'une solution à ton problème viendra plus naturellement.
    Sinon, le forum reste ouvert.

  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
    Citation Envoyé par Steph_ng8 Voir le message
    Une fonction template ne peut pas être virtuelle (je ne saurais pas t'expliquer clairement pourquoi).
    La première réponse qui me vienne est "Comment construire la vtable avec des fonctions génériques ?" Si elles ne sont pas instanciées, elles n'ont pas d'@. Et puis comment faire pour les instanciations des fonctions génériques avec des types non encore définis ?

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 30
    Par défaut
    J'ai du mal à comprendre l'impossibilité de méthodes templates virtuelles...

    Voilà un exemple de ce que je considère faisable, je vous laisse le soin ensuite de me descendre!

    Considérons le code suivant:
    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
    class Base{
     
    	template< typename T>
    	virtual do( T param){
    		doSomthing();
    	}
     
    };
     
    class Derived1{
    	template< typename T>
    	virtual do( T param ){
    		doSomethingElse();
    	}
     
    }
     
    class Derived2{
    	template <>
    	virtual do<int>(int param){
    		doWithInt(param);
    	}
    }
     
    int main(){
    	Base* d1 = new Derived1();
    	d1->do( 3.2f );
     
    	Base* d2 = new Derived2();
    	d2->do( 3.2f );
    	d2->do( 3 );
    }
    Pour moi, voici ce que le compilateur pourrait faire:
    lors du parsage de
    d1->do( 3.2f );
    il déclenche l'instanciation des différentes versions:
    Base::do<float>()
    Derived1::do<float>()
    La version la plus dérivée ( "Derived1::do<float>()" ) atterri dans la vtable

    De même pour d2, le parsage de
    d2->do( 3.2f );
    déclenche l'instanciation de:
    Base::do<float>()
    Derived2::do<float>()
    Ce dernier atterri dans la vtable, par le même proccessus que pour d1.

    Enfin, pour le cas où une spécialization est créée dans une classe héritée, la lecture de d2->do( 3 ); déclenche l'instanciation de:
    Base::do<int>()
    Derived2::do<int>(int param)
    Ce dernier atterri dans la vtable, étant la version la plus héritée.

    En gros, je suppose qu'il est possible, pour chaque type pour lequel la méthode doit être instanciée, de le faire pour toute la hiérarchie d'héritage, et de prendre la version la plus dérivée pour la vtable.

    Alors... Pourquoi est-ce que ca ne fonctionne pas?

  5. #5
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Le problème vient justement du fait que le compilateur ne connait pas quelles sont les classes filles de Base.
    Je pense que la compilation séparée est déjà une sacré limite.
    En prenant un exemple simple :
    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
     
    // fichier base.hpp
    class Base
    {
         template<class T>
        virtual void maFunc() {}
    };
     
    void use(Base& b);
     
    // fichier base.cpp
    #include "base.hpp"
     
    void use(Base& b)
    {
            base->maFunc<int>();
    }
     
    // fichier main.cpp
    #include "base.hpp"
     
    class Derivee
    {
         template<class T>
        virtual void maFunc() {}
    };
     
    int main()
    {
         Derive d;
         use( d );
    }
    Une fois l'étape du préprocesseur passée, tu as deux unité de compilation :
    • dans l'une, celle générée à partir de base.cpp, tu indique que la méthode maFunc doit être instanciée pour le T = int
    • dans l'autre, celle générée à partir de main.cpp, tu as une classe qui redéfinit maFunc


    Or chaque unité de compilation est, comme son nom l'indique, compilée séparément et dans un ordre quelconque. D'où le problème : comment le compilateur peut-il savoir lors de la compilation de main.cpp que dans une autre unité de compilation (qui peut avoir été compilée avant, ou ne sera compilée qu'après, ou même être en cours de compilation au même moment) une méthode template est utilisé avec un type donné (ici, int) ? Ou, en adoptant un autre point de vue, comment, à l'appel de la méthode maFunc dans la fonction use() (elle-même définie dans base.cpp), le compilateur peut-il savoir qu'il faut, dans une autre unité de compilation, instancier la méthode maFunc de certaines classes (ici Derivee) ?

  6. #6
    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,
    Comme le dit Joe, déjà la compilation séparée pose des questions. Mais quid si B hérite de A et n'instancie que f<T1> et C hérite de A et n'instancie que f<T2> ?? Et comment faire sur PC avec une DLL ?

  7. #7
    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,
    Citation Envoyé par Kaluza Voir le message
    Bonjour.

    Je réalise un programme de simulation avec Qt et je suis en train de concevoir une classe "AbstractComputation" destinée à réaliser l'interface entre Qt et les simulations. Ainsi, l'interface Qt interagira toujours avec des classes dérivées de AbstractComputation, tandis que les développeurs de simulation pourront faire ce qu'ils veulent comme simulation en dérivant leurs classes de AbstractComputation.

    Le problème est que je cherche à faire deux fonctions setData(QString identifier, T data) et getData(QString identifier) permettant de passer des données (par exemple les conditions initiales) entre Qt et entre les classes de simulation dérivées de AbstractComputation. Le mécanisme auquel j'ai pensé (mais qui n'est peut être pas le bon), est d'avoir ces deux fonctions
    setData et getData qui affectent le paramètre identifié par la "QString identifier" au bon datamember de la classe dérivée correspondant.

    1) Question 1 : est-ce la bonne façon de faire ?

    Mon problème est que ça m'arrangerai de faire pouvoir passer n'importe quel type de données, et donc d'utiliser un template. Or ce sont avec les classes dérivées de AbstractComputation que les setData et getData seront utilisés.

    2) Question 2 : du coup cela risque-t-il de poser problème si getData<double> n'a pas été instancié au niveau de AbstractComputation mais l'a été via une classe dérivée ?

    Dites le moi si tout cela n'était pas très clair, que j'essaye de reformuler ça.

    Merci beaucoup
    J'opte pour un problème de design :
    => Soit la manipulation des données est légale dans la classe de base et alors pas besoin de fonctions virtuelles.
    => soit les données sont spécifiques à une classe dérivée. Alors cela suppose forcément que tu connais la classe dérivée lorsque tu manipules ces données. Donc non pas via l'interface abstraite mais via l'interface dérivée. Et à ce moment, la fonction n'a pas besoin d'être virtuelle non plus.

    (bonus : get/set sont souvent indicateur de rupture de l'encapsulation)

Discussions similaires

  1. methode abstraite vs methode virtual dans classe abstraite
    Par mapmip dans le forum Général Dotnet
    Réponses: 1
    Dernier message: 17/05/2013, 00h49
  2. Accès Variable template dans classe
    Par fex79 dans le forum C++
    Réponses: 10
    Dernier message: 03/01/2012, 09h20
  3. Syntaxe fonction template dans classe template
    Par Aleph69 dans le forum C++
    Réponses: 6
    Dernier message: 15/07/2011, 15h32
  4. Méthode template dans classe non template ?
    Par shenron666 dans le forum Langage
    Réponses: 12
    Dernier message: 04/09/2006, 17h50
  5. OnInitDialog() dans classe abstraite
    Par rigobert dans le forum MFC
    Réponses: 5
    Dernier message: 09/08/2006, 10h52

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