Partage
  • Partager sur Facebook
  • Partager sur Twitter

Problème avec les templates et l'héritage

    22 février 2012 à 14:05:13

    Bonjour à tous,

    C'est mon premier post sur ce site.
    Je suis actuellement en train d'écrire une bibliothèque pour le Data Mining.
    À la base je viens du monde Java et après quelques tests de performance et de consommation mémoire j'ai décidé de me tourner vers le C++ qui semble bien plus efficace pour ce type d'application. J'apprends le C++ sur le tas et je pense avoir plutôt bien pigé les concepts qui sont quand même assez proche de Java mais avec énormement de petites subtilités.
    Cette bibliothèque s'appuie énormément sur l'utilisation de templates avec définitions de conteneurs, structures de données et algorithmes génériques.

    Mais j'ai un pseudo problème (c'est pas vraiment bloquant) que je ne comprends pas trop et je vais essayer d'être le plus clair possible. Je n'ai bizarrement pas trouvé de solution pour ce problème qui je pense est plutôt courant.
    1 - J'ai défini un conteneur template List<A> qui est tout simplement un héritage de vector<A> mais avec encapsulation de méthodes ensemblistes qui me sont bien utiles (union, intersection, ...)
    2 - J'ai une structure de données Sequence<B> qui est un conteneur dont les élèments sont ordonnés (surcharges des opérateurs de comparaisons obligatoire pour l'élément de type B) et qui encapsule aussi les opérations ensemblistes.
    3 - Une structure de données ContextualSequence<B> qui hérite de Sequence<B> et d'une interface IContextualFeature qui lui donne un comportement bien particulier (rentrer dans les détails serait trop long).
    4 - J'ai un algorithme template MatrixBuilder::similarityMatrix<C>(List<C>, Metric<C>) qui prend en paramètre une List<C> et une métrique Metric<C> et retourne une matrice Matrix<C,C,float>. Concrètement, cet algorithme calcule une matrice de similarité entre tous les élèments de List<C> et a donc besoin d'une métrique (une mesure de distance/similarité) entre deux éléments C.
    En gros cet algorithme utilise un design pattern 'Strategy' qui permet d'exterioriser une partie de code d'un algorithme (e.g. le Collection.sort() en Java).
    5 - J'ai définie une méthode de calcul de similarité (i.e. métrique) entre deux séquences d'itemsets (i.e. Sequence<Set<B>*>) et qui s'appelle DTWSI<B> dans l'objectif d'utiliser MatrixBuilder::similarityMatrix.

    Problème:
    Tout fonctionne très bien si j'envoie en paramètre une List<Sequence<Set<B>*>*> et la métrique DTWSI<B>.
    Par contre le compilo me gueule dessus (GCC) si j'envoie en paramètre une List<ContextualSequence<Set<B>*>*> avec DTWSI<B> et me dit qu'il attend des 'Sequence' et pas des 'ContextualSequence' alors que les 'ContextualSequence' sont des 'Sequence' par héritage, donc je ne comprends pas trop.
    Voici le code qui ne compile pas:
    typedef ContextualSequence< Set< unsigned char >*> DataSeq;
    typedef Sequence< Set< unsigned char >*> Seq;
    
    int main(int argc, char **argv) {
        
        List< DataSeq* >* listSequence =    
            &SequenceBuilder::loadFromDnaMicroArrays("Dataset/Breast_Cancer:50_1-3.csv");
        
        DTWSI<unsigned char> metric;
        Matrix< Seq,Seq,float>* matrix = 
            &MatrixBuiler::similarityMatrix< Seq >(*listSequence,metric);
        
        cout << *matrix << endl;
    
        delete listSequence;
        delete matrix;
    }
    


    Si je veux que cela compile je dois créér une nouvelle liste de type List<Sequence<Set<B>*>*> dans laquelle j'insère les 'ContextualSequence' de la première liste. Alors même si ça ne me gène pas plus que ça, pour le moment, je trouve ça plutôt lourd et me dérange un petit peu :-° .
    De plus ma librairie évolue très vite et cela pourrait devenir un peu plus problématique car j'ai d'autres algorithmes qui fonctionnent à des niveaux d'abstraction bien plus important (e.g. par les interfaces).
    Voici le code qui compile:

    typedef ContextualSequence< Set< unsigned char >*> DataSeq;
    typedef Sequence< Set< unsigned char >*> Seq;
    
    int main(int argc, char **argv) {
        
        List< DataSeq* >* listSequence1 =    
            &SequenceBuilder::loadFromDnaMicroArrays("Dataset/Breast_Cancer:50_1-3.csv");
            
        List< Seq* >* listSequence2 = new List< Seq* >;
        
        List< DataSeq* >::const_iterator it;
        for(it = listSequence1->begin() ; it != listSequence1->end() ; ++it) {
            listSequence2->push_back(*it);
        }
        
        DTWSI<unsigned char> metric;
        Matrix< Seq,Seq,float>* matrix = 
            &MatrixBuiler::similarityMatrix< Seq >(*listSequence2,metric);
        
        cout << *matrix << endl;
    
        delete listSequence1;
        delete listSequence2;
        delete matrix;
    }
    


    Je souhaitais donc savoir s'il y a une manière élégante de gérer ce soucis sans faire de cast ou autres atrocités. :D

    Merci bien pour votre aide,
    Bonne journée,
    Mikkou
    • Partager sur Facebook
    • Partager sur Twitter
    Anonyme
      22 février 2012 à 14:14:41

      Citation


      typedef ContextualSequence< Set< unsigned char >*> DataSeq;
      typedef Sequence< Set< unsigned char >*> Seq;
      
      int main(int argc, char **argv) {
          
          List< DataSeq* >* listSequence =    
              &SequenceBuilder::loadFromDnaMicroArrays("Dataset/Breast_Cancer:50_1-3.csv");
          
          DTWSI<unsigned char> metric;
          Matrix< Seq,Seq,float>* matrix = 
              &MatrixBuiler::similarityMatrix< Seq >(*listSequence,metric);
          
          cout << *matrix << endl;
      
          delete listSequence;
          delete matrix;
      }
      




      c'est normal que le compilo te dise ça, tu utilise ta méthode template sur des Seq (type Sequence) en passant en paramètre à ta méthode un type contextualSequence

      ça donne quoi si tu lui passe un contextualSequence au niveau du template ?

      Matrix< DataSeq,DataSeq,float>* matrix =
      &MatrixBuiler::similarityMatrix< DataSeq >(*listSequence,metric);


      Sinon tu peux peut-être t'en sortir en faisant un static_cast
      • Partager sur Facebook
      • Partager sur Twitter
        22 février 2012 à 14:29:35

        Merci pour ta réponse.
        Si je lui passe un ContextualSequence dans le template cela ne fonctionne pas car ma métrique est définie sur une Sequence et non une ContextualSequence.
        Ce que je veux c'est que mon algorithme, couplé avec ma métrique, puisse s'appliquer à tous mes types de Sequence sans faire de cast.
        Ici j'ai présenté ContextualSequence mais il y en a bien d'autres.
        Mon application est séparée en plusieurs couches :
        - J'ai mes structures de données : List<T>, Sequence<T>, Set<T>, Cluster<T>, etc...
        - J'ai des classes abstraites/interfaces qui définissent des comportements : IContextualFeature, IFrequentFeature, IClassifiedFeature, etc...
        - J'ai mes structures avec comportements : ContextualSequence<T>, FrequentSequence<T>, etc...

        En gros cet algorithme, couplé avec ma métrique, fonctionne avec des Sequence mais d'autres algorithme vont fonctionner avec des IFrequentFeature par exemple.

        Edit: Je viens de voir ta réponse sur le static_cast, je peux essayer mais j'ai justement utilisé les templates pour éviter de faire ce genre de choses.
        • Partager sur Facebook
        • Partager sur Twitter
          22 février 2012 à 14:49:18

          1\ On n'hérite pas publiquement des conteneurs, ce n'est pas fait pour ca et ca n'a pas de sens (c'est une relation HAS_A pas IS_A).
          3\ Les fonctions ensemblistes ont bien plus de sens en étant libre que membre (binaire).
          3\ Une List<A*> n'est pas une List<B*> même si A hérite de B.
          • Partager sur Facebook
          • Partager sur Twitter
          FaQ : Fr | En 1 2 | C++11 | Template || Blog : Deloget | C++|Boost--Dev | C++Next | GotW || Installer Boost
            22 février 2012 à 15:11:24

            Citation : Freedom

            1\ On n'hérite pas publiquement des conteneurs, ce n'est pas fait pour ca et ca n'a pas de sens (c'est une relation HAS_A pas IS_A).


            Je ne vois pas en quoi ça n'a pas de sens. Si je fais une classe A<T> qui est composée d'un vector<T> en private et où je dois redéfinir toutes les opérations pour accéder aux opérations du vector lui même, cela revient au même que de faire un héritage car je veux justement que ma classe A<T> soit un vector avec des opérations suplémentaires.

            Citation : Freedom

            1\ 3\ Les fonctions ensemblistes ont bien plus de sens en étant libre que membre (binaire).


            Certe, c'est une question de gouts.

            Citation : Freedom

            3\ Une List<A*> n'est pas une List<B*> même si A hérite de B.


            C'est du polymorphisme d'héritage, une fonction qui prend en paramètre une List<B*> doit pouvoir prendre une List<A*> ca me parait logique. Cela reviendrait à dire qu'un paquet de pépitos n'est pas un paquet de biscuits même si un pépito est un biscuit ?
            • Partager sur Facebook
            • Partager sur Twitter
              22 février 2012 à 15:33:56

              1\ Destructeur des conteneurs standard non virtuel, en hériter publiquement c'est insérer un UB potentiel dans le code. Si tu as fait un héritage juste pour importer des fonctions, alors fait un héritage privé et utilise using.

              2\ Non, de conception. Les fonctions membres servent à définir les services de bases de ta classe, ceux qui servent à abstraire totalement l'implémentation. C'est clairement pas le cas des opérateurs ensemblistes.

              3\ Si un paquet de pepitos était un paquet de biscuit alors je pourrais mettre un biscuit dans un paquet de pepitos, dans ce cas ca ne serait plus un paquet de pepitos, donc un paquet de pepitos n'est pas un paquet de biscuit (en conception OO du moins).
              • Partager sur Facebook
              • Partager sur Twitter
              FaQ : Fr | En 1 2 | C++11 | Template || Blog : Deloget | C++|Boost--Dev | C++Next | GotW || Installer Boost
                22 février 2012 à 15:39:30

                Je ne peux qu'appuyer les remarques de Freedom.
                • Partager sur Facebook
                • Partager sur Twitter
                Je ne suis responsable que de ce que je dis, pas de ce que vous comprenez... - /!\ Négligences de sécurité sur OpenClassrooms /!\
                Anonyme
                  22 février 2012 à 16:01:47

                  sans m'occuper de ce que tu esasie de faire (mais je suis aussi d'accord avec Freedom, les conteneurs ne sont pas fait pour avoir ce genre de relation), tu peux tout à fait avoir besoin d'avoir une liste d'un peu tout et n'importe quoi, avec tout et n'importe quoi qui hérite de la même chose. Pour cela, la meilleur solution est de caster dans ton cas je penses, histoire d'avoir accès à toutes les spécialisations de tes classes filles.
                  • Partager sur Facebook
                  • Partager sur Twitter
                    22 février 2012 à 16:25:27

                    Ah. J'avais zappé que cela avait été posté ici aussi. ^^'
                    Ma réponse -> http://forum.ubuntu-fr.org/viewtopic.php?id=830141
                    (autant continuer ici par la même occasion)
                    • Partager sur Facebook
                    • Partager sur Twitter
                    C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                      22 février 2012 à 16:49:25

                      Ok merci pour toutes vos remarques.

                      Citation

                      1\ Destructeur des conteneurs standard non virtuel, en hériter publiquement c'est insérer un UB potentiel dans le code. Si tu as fait un héritage juste pour importer des fonctions, alors fait un héritage privé et utilise using.


                      Ok merci je ne savais pas.

                      Citation


                      3\ Si un paquet de pepitos était un paquet de biscuit alors je pourrais mettre un biscuit dans un paquet de pepitos, dans ce cas ca ne serait plus un paquet de pepitos, donc un paquet de pepitos n'est pas un paquet de biscuit (en conception OO du moins).


                      Oui je vois le bien le truc maintenant.

                      Pour le problème de slicing dont parle lmghs dans mon autre poste c'est quoi au juste.
                      Si j'ai une List<A> et B héritant de A, si j'insère une instance de B dans la liste je perds de l'information puisque les attributs qui sont propre à B ne seront pas conservés ?
                      • Partager sur Facebook
                      • Partager sur Twitter
                        22 février 2012 à 16:55:13

                        Oui c'est ca le slicing. Mais de toute facon si A hérite de B alors A et B ne sont probablement pas copiable et donc tu peux pas faire de list<A> et list<B>, faut passer par des pointeurs.
                        • Partager sur Facebook
                        • Partager sur Twitter
                        FaQ : Fr | En 1 2 | C++11 | Template || Blog : Deloget | C++|Boost--Dev | C++Next | GotW || Installer Boost
                          22 février 2012 à 17:17:53

                          Oui en effet, du coup le slicing ne concerne pas les pointeurs si je comprends bien.
                          Concernant l'héritage des conteneurs de base qui est déconseillé, vous auriez un lien vers un article qui explique toutes ces raisons ? Merci
                          • Partager sur Facebook
                          • Partager sur Twitter
                            22 février 2012 à 19:23:43

                            Eh bien merci pour tous vos conseils.
                            Je pense que je vais mettre le vector comme attribut du coup.
                            Vu que je ne pense utiliser que des pointeurs dans mes listes, sets, etc... je suis en train de regarder les conteneurs du type ptr_vector de la librairie Boost.
                            Si ça peut intéresser quelqu'un j'ai aussi trouvé un article assez intéressant sur la définition de conteneurs de pointeurs avec une gestion du polymorphisme plutôt simpa.
                            Par exemple voici le code pour itérer dans un conteneur seulement sur les éléments d'un certain type :
                            typedef PtrVector<Sequence> sequenceVector;
                            typedef sequenceVector::Iterator<ContextualSequence> contextualIterator;
                            
                            sequenceVector vector;
                            ...
                            for ( contextualIterator iterator = vector.begin<ContextualSequence>();
                            iterator != vector.begin<ContextualSequence>();
                            ++ iterator )
                            {
                            ... // opérations sur les intances de type ContextualSequence
                            }
                            
                            // calcule le nombre total d'éléments de type ContextualSequence dans le vector
                            std::size_t nbContextualSequence = vector.size< ContextualSequence >();
                            


                            Bonne soirée
                            • Partager sur Facebook
                            • Partager sur Twitter
                              23 février 2012 à 1:34:31

                              Citation : Mikkou

                              Concernant l'héritage des conteneurs de base qui est déconseillé, vous auriez un lien vers un article qui explique toutes ces raisons ?


                              Voir aussi: http://punchlet.wordpress.com/2009/12/ [...] er-the-fifth/
                              • Partager sur Facebook
                              • Partager sur Twitter
                              C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                                23 février 2012 à 10:01:00

                                Citation

                                Voir aussi: http://punchlet.wordpress.com/2009/12/ [...] er-the-fifth/


                                Super article ! En plus d'être pédagogique il m'a bien fait marrer, j'adore le style.
                                • Partager sur Facebook
                                • Partager sur Twitter

                                Problème avec les templates et l'héritage

                                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                • Editeur
                                • Markdown