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

SL & STL C++ Discussion :

Itérateurs " génériques "


Sujet :

SL & STL C++

  1. #1
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut Itérateurs " génériques "
    Bonjour,

    J'ai actuellement une interface A dont une méthode prend un vecteur de X et une autre interface B retournant un vecteur de X.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class A
    {
                   virtual void foo(const std::vector<X> &) = 0;
    }
     
    class B
    {
                  virtual const std::vector<X> & foo(void) = 0;
    }
    Vous l'aurez compris, le but est de faire :
    Mais j'aimerais faire quelque chose de plus générique pour permettre aux classes implémentant l'interface B d'utiliser d'autres conteneurs qu'un std::vector<X>.
    J'aimerais donc avoir quelque chose du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A
    {
                   virtual void foo(constIterator<X>, constIterator<X>) = 0;
    }
     
    class B
    {
                  virtual std::pair<constIterator<X>, constIterator<X> > foo(void) = 0;
                  // ou const IConteneur & foo(void) = 0; où IConteneur n'a que deux méthodes : begin() et end(). 
    }
    En effet, dans A, je dois juste parcourir le conteneur, et je me moque un peu de son type.
    Pour faire cela, la STL utilise des templates (cf std::vector::vector() ).

    Or, je ne suis pas sûr que les templates "virtuelles" existent .
    Je me demandais donc si la STL n'avais pas une solution à ce niveau-là ou s'il n'existait pas un idiome ou un DP pour ceci.

  2. #2
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Bonjour,

    Il existe peut-être une solution, mais pour ça il faudrait que tu nous montres pourquoi ces fonctions doivent être virtuelles. Si tu peux utiliser boost, je t'invite à regarder boost.range qui contient ce que tu as besoin (any_range).

  3. #3
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    il faudrait que tu nous montres pourquoi ces fonctions doivent être virtuelles.
    Parce que A et B sont des interfaces .

    Je donne leurs interfaces et derrière, je n'ai pas d'à priori sur la manière dont ils seront implémentés.
    Je peux même avoir plusieurs implémentation et choisir celle que je veux.

    Derrière, je vais avoir un DP façade qui va utiliser les deux interfaces, mais je pourrais donner à cette façade l'implémentation que je souhaite.

    Citation Envoyé par Flob90 Voir le message
    Si tu peux utiliser boost, je t'invite à regarder boost.range qui contient ce que tu as besoin (any_range).
    Je ne peux malheureusement qu'utiliser la bibliothèque standard.

  4. #4
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Ceci dit boost.range est header only, donc tu peux toujours isoler et inclure dans ton projet directement les fichiers de boost.range.

    Sans ça, il n'y a rien de standard pour ça actuellement (boost.range, du moins l'idée qui est derrière, sera surement standard en C++17, mais je ne crois pas qu'un type erasure pour le concept de range soit prévu actuellement).

    Si tu veux le faire à la main, il s'agit de mettre en place un type erasure qui convient à tes besoins. Tu auras des infos sur des implémentations de ce genre en cherchant any_iterator et any_range sur google.

    AMA, c'est une erreur que ces fonctions soient virtuelles. Je te suggérerais bien des alternatives mais il faut plus de détail sur la situation réelle.

  5. #5
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    En gros, j'ai 5 interfaces :
    • moduleFileSystem : gère le "chemin" des modules dans le système de fichier ;
    • moduleInfo : donne les informations sur un module ;
    • moduleDependancies : on lui donne une liste de modules et il établit l'ordre de chargement des modules selon leurs dépendances ;
    • moduleLoader : charge les modules ;
    • moduleManager : façade.


    Donc, je vais avoir des méthodes qui vont retourner des ensembles :
    • moduleFileSystem::modulesEnabled() : ensemble des modules "activé" ie contenu dans un dossier particulier ;
    • moduleInfo::loadInfo() : donne la liste des informations des modules dont les chemins sont passés en paramètre ;
    • moduleDependancies::computeOrder() : donne l'ordre de chargement des modules dont les informations sont passés en paramètres ;


    Et je vais avoir des méthodes qui vont prendre en entrée des ensembles :
    • moduleInfo::loadInfo()
    • moduleDependancies::computeOrder()
    • moduleLoader::loadModules()


    Chaque classe étant totalement indépendante, ce qui est logique :
    • la façon de stocker les modules peut varier (dossiers, fichier de configuration, BDD, etc.) ;
    • la façon de récupérer les informations et de les stocker pour un accès futur n'a rien à voir avec la manière dont les modules sont stockés ;
    • le calcul de l'ordre de chargement selon les dépendance est un algorithme qui n'a pas à savoir comment récupérer les informations ou de savoir comment les modules sont stockés.
      On peut aussi trouver d'autres algorithmes ou d'autres façon de gérer certains problèmes de dépendances.
    • Le chargement des modules est bête et méchant.


    Ainsi chacune de mes classes a une responsabilité unique, simple et bien définie.
    Je peux ainsi changer au sein de ma façade certains composants.

    C'est donc pour cela que j'ai besoin d'interfaces.

  6. #6
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    J'ai trouvé un exemple de type erasure ici.

    Si j'ai bien compris on fait :
    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
    class Iterator
    {
          class IteratorConcept
          {
                virtual void foo(void)  = 0;
          };
     
          template<typename T>
          class IteratorConcrete : public IteratorConcept
          {
               IteratorConcrete(const T &) : T(it){}
               virtual void foo(void){ it.foo(); }
               T it;
          };
     
          IteratorConcept * m_ptr;
          public :
               void foo(void){ m_ptr->foo(); }
     
               template<typename T>
               Iterator( const T & ptr) : m_ptr(new IteratorConcrete<T>(ptr){}
    }
    Par contre, je ne comprend pas pourquoi on encapsule "IteratorConcept" dans la classe "Iterator".
    C'est pour permettre la copie de "Iterator" ?

  7. #7
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Et tu vas stocker dans des conteneurs des pointeurs de moduleFileSystem (ou une autre de tes interfaces) ? Si ce n'est pas le cas, je ne vois aucune raison de faire une classe de base. Une définition de chacun de ces concepts bien documenté suffit.

    Tu n'es pas obliger de mettre la classe dedans, personnellement j'évite des classes internes à d'autre classe pour un type erasure.
    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
     
    struct base_iterator
    { 
      /*ici les fonctions virtuelles pures qui conviennent pour un itérateur*/ 
      virtual void foo() =0;
    };
     
    template<class Iterator>
    struct concrete_iterator : base_iterator
    {
      /*ici implémentation des fonctions virtuelles pures en utilisant it*/
      void foo()
      { it.foo(); }
     
    private:
      Iterator it;
    };
     
    struct any_iterator
    {
      template<class Iterator>
      any_iterator(Iterator&& it)
        : ptr(std::make_unique<concrete_iterator<Iterator>>(std::forward(it)))
      { }
      /*implémentation des fonctions non virtuelles qui conviennent à un itérateur*/
      void foo()
      { ptr->foo(); }
     
    private:
      std::unique_ptr<base_iterator> ptr;
    };
    Tu peux rajouter des paramètres dans la classe de base tant que tu les rajoutes partout.

  8. #8
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Et tu vas stocker dans des conteneurs des pointeurs de moduleFileSystem (ou une autre de tes interfaces) ?
    Oui, la façade contiendra un pointeur sur une implémentation pour chaque interfaces.

  9. #9
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Donc moduleManager ressemble à ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class moduleManager
    {
      template<class T>
      using container = std::vector<std::unique_ptr<T>>;
     
      container<moduleFileSystem> file_system;
      container<moduleInfo> info;
      container<moduleDependancies> dependancies;
      container<moduleLoader> loader;
    };
    Ou à ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class moduleManager
    {
      std::unique_ptr<moduleFileSystem> file_system;
      std::unique_ptr<moduleInfo> info;
      std::unique_ptr<moduleDependancies> dependancies;
      std::unique_ptr<moduleLoader> loader;
    };
    ?

  10. #10
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    ModuleManager ressemble à ta seconde version (avec les uniques_ptr )

    J'ai sauté un mot dans ta précédante question, non je ne vais pas metre mes interfaces dans des containers.

  11. #11
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Dans ce cas, pourquoi ne pas templater :
    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 FileSystem,
      class Info,
      class Dependancies,
      class Loader
    >
    class moduleManager
    {
      FileSystem file_system;
      Info info;
      Dependancies dependancies;
      Loader loader;
    };
     
    //Avec constructeur et fonction de création externe pour rendre l'utilisation facile
    Ou pas loin ? Et dans ce cas plus besoin de fonctions virtuelles.

  12. #12
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Généralement, j'essaye d'éviter le plus possible les DP politiques :
    • On ne va pas avoir la notion que ModuleFileSystemA, ModuleFileSystemBDD sont des ModuleFileSystem ;
    • Au niveau de l'implémentation d'un ModuleFileSystem, c'est moins "direct" ;
    • Je suis obligé de mettre ModuleManager en header-only ;
    • Si je veux choisir (ou créer) le ModuleFileSystem au sein d'une fonction avant de créer le ModuleManager, ce ne sera pas possible :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ModuleManager<A,B,C, ?> m(A(), B(), C(), chooseD() );
    • De plus, est-ce que ModuleManager a vraiment à connaître le type exact de ModuleFileSystem ? Si je template ModuleFileSystem pour choisir le container interne à utiliser, cela me parait pas super de devoir faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ModuleManager<A,B,C, ModuleFileSystemT<std::vector>>(...)
    J'ai l'impression de briser d'une certaine manière l'encapsulation.

    Et parfois y'a des choses bizarres :
    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
    #include <iostream>
     
    template <typename T>
    class U
    {
            public :
                    U(const T & t)
                    {
                            std::cerr << "o" << std::endl;
                            t.foo();
                    }
    };
     
    class K{};
     
    int main(void)
    {
            //U<int>(45);
            std::cerr << "!" << std::endl;
            U<K> t( K() );
            std::cerr << "?" << std::endl;
    }
    Affiche :
    !
    ?

  13. #13
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Généralement, j'essaye d'éviter le plus possible les DP politiques :
    • On ne va pas avoir la notion que ModuleFileSystemA, ModuleFileSystemBDD sont des ModuleFileSystem ;
    • Au niveau de l'implémentation d'un ModuleFileSystem, c'est moins "direct" ;
    • Je suis obligé de mettre ModuleManager en header-only ;
    • Si je veux choisir (ou créer) le ModuleFileSystem au sein d'une fonction avant de créer le ModuleManager, ce ne sera pas possible :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ModuleManager<A,B,C, ?> m(A(), B(), C(), chooseD() );
    • De plus, est-ce que ModuleManager a vraiment à connaître le type exact de ModuleFileSystem ? Si je template ModuleFileSystem pour choisir le container interne à utiliser, cela me parait pas super de devoir faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ModuleManager<A,B,C, ModuleFileSystemT<std::vector>>(...)
    J'ai l'impression de briser d'une certaine manière l'encapsulation.

    Et parfois y'a des choses bizarres :
    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
    #include <iostream>
     
    template <typename T>
    class U
    {
            public :
                    U(const T & t)
                    {
                            std::cerr << "o" << std::endl;
                            t.foo();
                    }
    };
     
    class K{};
     
    int main(void)
    {
            //U<int>(45);
            std::cerr << "!" << std::endl;
            U<K> t( K() );
            std::cerr << "?" << std::endl;
    }
    Affiche :
    • Et ? La substituabilité n'est utile que si tu l'utilises effectivement, sinon elle vient juste ajouter la contrainte des fonctions virtuelles. C'est la raison pour laquelle je t'ai demandé si tu utilisais des conteneurs.
    • Je ne comprends pas cet argument, tu entends quoi par moins direct ?
    • Et ? En quoi est-ce une contrainte ?
    • Bien sur que si, il faut créer une fonction de création externe à Manage comme je l'ai indiqué en commentaire :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
       
      template</*stuff*/>
      Manager</*stuff*/> make_manage(/*stuff*/)
      { return Manager</*stuff*/>(/*stuff*/); }
    • Si tu les vois comme des politiques (et donc les descriptions des classes de base que tu m'as indiques dans un message précédent deviennent les descriptions des concepts régissant ces politiques), ce n'est pas un problème que Manager connaissent les types utilisés, tu ne brises rien. Pour l'utilisation, ton argument ne tient pas, il faut profiter de la déduction des paramètres template au maximum et tu n'as presque jamais a indiquer les paramètres template (comme avec make_manager). Pour ton morceau de code, personnellement il m'affiche :
      error: parentheses were disambiguated as a function declaration [-Werror,-Wvexing-parse]
      U<K> t( K() );
      ^~~~~~~
      note: add a pair of parentheses to declare a variable
      U<K> t( K() );
      ^
      ( )

    De manière générale, j'ai tendance à conserver les types tant que je peux les garder, je ne vais les passer derrière un type-erasure (que ce soit une simple classe de base avec une fonction virtuelle ou une construction plus complexe) que si le besoin devient réel. Le typage c'est l'information qu'on donne au compilateur sur ce qu'on fait, brider le type avec des type-erasure c'est en dire moins au compilateur, quel intérêt ?

  14. #14
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Et ? La substituabilité n'est utile que si tu l'utilises effectivement
    Mais ce serait une erreur de s'en priver si on a pas une raison.
    Si dans trois semaine j'ai besoin de la substituabilité, ce serait dommage de devoir réécrire mon code.

    [QUOTE=Flob90;8045440]Je ne comprends pas cet argument, tu entends quoi par moins direct ?[quote]
    Quand on voit que la méthode prend un IMachin, il suffit d'hériter de IMachin et de redéfinir ses méthodes. Si on ne le fait pas, le compilateur criera au scandale.

    En revanche, dans le cas d'un template, il faudra aller rechercher ce dont on aura besoin (parcourir la doc si elle existe). Mais il est aussi possible de ne pas définir une méthode non utilisée par ModuleManager mais qui pourtant fait parti de l'interface.
    Ainsi, il serait possible, lors d'une modification de ModuleManager d'avoir des ModuleFileSystem qui ne "marcheront plus".

    Bien sur que si, il faut créer une fonction de création externe à Manage comme je l'ai indiqué en commentaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    template</*stuff*/>
    Manager</*stuff*/> make_manage(/*stuff*/)
    { return Manager</*stuff*/>(/*stuff*/); }
    Ce n'est pas une solution, ici tu crée une factory de Manager, mais tu ne peux pas créer de factory spécifique pour ses différents composants.

    De manière générale, j'ai tendance à conserver les types tant que je peux les garder, je ne vais les passer derrière un type-erasure (que ce soit une simple classe de base avec une fonction virtuelle ou une construction plus complexe) que si le besoin devient réel. Le typage c'est l'information qu'on donne au compilateur sur ce qu'on fait, brider le type avec des type-erasure c'est en dire moins au compilateur, quel intérêt ?
    Parce qu'il n'a pas besoin de savoir le type réel.

    Que ce soit des itérateurs de vecteurs, de list, de deque, de map, de set, on s'en moque.
    je préfère éviter les templates dès que je le peux, ils ont la mauvaise habitude de se "propager" (cf. ta fonction make_manage qui est elle-même template).

    Je pense donc partir sur un type-erasure.

  15. #15
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Mais ce serait une erreur de s'en priver si on a pas une raison.
    Si dans trois semaine j'ai besoin de la substituabilité, ce serait dommage de devoir réécrire mon code.
    Si tu n'as pas bâclé ta réflexion, tu dois savoir maintenant si c'est nécessaire ou pas, pas dans trois semaines.
    [QUOTE=Neckara;8045501]
    [QUOTE=Flob90;8045440]Je ne comprends pas cet argument, tu entends quoi par moins direct ?
    Quand on voit que la méthode prend un IMachin, il suffit d'hériter de IMachin et de redéfinir ses méthodes. Si on ne le fait pas, le compilateur criera au scandale.

    En revanche, dans le cas d'un template, il faudra aller rechercher ce dont on aura besoin (parcourir la doc si elle existe). Mais il est aussi possible de ne pas définir une méthode non utilisée par ModuleManager mais qui pourtant fait parti de l'interface.
    Ainsi, il serait possible, lors d'une modification de ModuleManager d'avoir des ModuleFileSystem qui ne "marcheront plus".
    En effet il manque actuellement de quoi donné de la sémantique à l'idée de concept en C++, ça arrivera en C++17. Je préfère cependant développer en programmation par concept malgré l'absence d'un outil que d'attendre trois ans.
    Citation Envoyé par Neckara Voir le message
    Ce n'est pas une solution, ici tu crée une factory de Manager, mais tu ne peux pas créer de factory spécifique pour ses différents composants.
    C'est la solution idiomatique à la création d'objets issues de classe template. Qu'entends tu dans la fin de cette phrase ?
    Citation Envoyé par Neckara Voir le message
    Parce qu'il n'a pas besoin de savoir le type réel.

    Que ce soit des itérateurs de vecteurs, de list, de deque, de map, de set, on s'en moque.
    je préfère éviter les templates dès que je le peux, ils ont la mauvaise habitude de se "propager" (cf. ta fonction make_manage qui est elle-même template).
    Très mauvais exemple, les algorithmes prennent en paramètre des template pour conserver le type exacte, ils ne se moquent pas du "type réel" comme tu sembles le penser. En effet ils se propagent et en quoi est-ce un problème ?

  16. #16
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Si tu n'as pas bâclé ta réflexion, tu dois savoir maintenant si c'est nécessaire ou pas, pas dans trois semaines.
    Pas nécessairement, un code doit rester "évolutif".
    De plus, je ne peux donner des informations qu'en ce qui concerne l'utilisation que j'en ferais, mais rien n'empêche un autre de reprendre mon code et d'avoir une autre utilisation.

    Ensuite, des imprévus peuvent toujours arriver : un nouveau besoin, une solution qui finalement ne marche pas ou qui n'est pas satisfaisante, etc.

    Citation Envoyé par Neckara Voir le message
    C'est la solution idiomatique à la création d'objets issues de classe template. Qu'entends tu dans la fin de cette phrase ?
    Je ne peux pas avoir de fonctions du type :
    chooseD() ou chooseC() car je ne saurais pas quel type retourner.

    Très mauvais exemple, les algorithmes prennent en paramètre des template pour conserver le type exacte, ils ne se moquent pas du "type réel" comme tu sembles le penser.
    Les algorithmes en eux-même se moquent du type, sinon on n'aurait pas un template.
    En revanche, on va, via le template, créer une fonction par "type" dès qu'on en aura le besoin.

    Mais dans le template en lui-même, qu'on lui passe un A, un B ou un C, le code qu'on aura nous-même écrit sera le même (mais différent dans "fonctions générées").
    Je ne prend pas en compte la spécialisation de template, qui d'ailleurs est un problème supplémentaire ici.

    En effet ils se propagent et en quoi est-ce un problème ?
    Le fait même qu'ils se propagent est un problème, en effet, cela propage des contraintes liés au fait d'être template.
    On va avoir des distinctions entre A<X> et A<Y> qui n'ont peut être aucun sens.
    Si A contient un B, B contient un C, etc. cela me gène que l'implémentation d'un 'Z' ait une influence sur 'A'.

    D'autant plus qu'un A a; pourrait devenir A<B<C<...<X>...> > > a; ou A<X> a; .
    D'autant plus qu'un A<X> a ne pourra pas devenir un A<Y> a.

  17. #17
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Les algorithmes utilisent du tag dispatching, donc non ils ne se moquent pas du type réel quoique tu en penses.

    Étrangement tu rechignes à utiliser des template alors que tu en abuses lors de l'utilisation de la bibliothèque standard (qui utilisent assez peu de fonction virtuelle et d'héritage d'ailleurs), tu ne trouves pas ça paradoxale ?

    Pour le coté évolutif, on peut toujours rajouter un type erasure (comme tu vas le faire pour les itérateurs, d'ailleurs prend la norme pour ne rien n'oublier d'implémenter), par contre si tu me mets un héritage et des fonctions virtuelles, tu me "forces" à payer le coût (bien que faible j'en convient).

    L'introduction d'une classe de base c'est un type erasure intrusif, et comme tout ce qui est intrusif, ce n'est à introduire que si c'est nécessaire. Ceci n'étant que mon avis.

  18. #18
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Les algorithmes utilisent du tag dispatching, donc non ils ne se moquent pas du type réel quoique tu en penses.
    Je ne sais pas vraiment ce qu'est le "tag dispatching", mais quand tu écris :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template typename<T> foo(void)
    {
          T a;
          [...]
          std::cerr << a << std::endl;
    }
    Tu te moques que T soit un int, un double, un machin, un bidule ou un whatever.
    Quand tu écris ce code, tu n'as pas d'à priori sur le type "T" qui sera donné, tu sauras juste qu'il faudra que "std::cerr << a" doit compiler donc un à priori sur la manière dont on doit pouvoir manipuler "T" mais rien de plus.

    L'algorithme est donc indépendant du type de T, c'est un peu l'objectif des templates, non ? (hors spécialisation).

    En revanche, quand tu écris :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    template <typename int> foo(void);
    //ou
    foo<int>();
    Là le compilateur ne va pas se moquer du type de "T" pour écrire ta fonction.

    Étrangement tu rechignes à utiliser des template alors que tu en abuses lors de l'utilisation de la bibliothèque standard (qui utilisent assez peu de fonction virtuelle et d'héritage d'ailleurs), tu ne trouves pas ça paradoxale ?
    Non, ce n'est pas parce que j'utilise la STL que cela doit me servir d'arguments à utiliser des templates de partout :
    • j'ai déjà vu des codes qui abusaient de templates, c'est totalement illisible et repoussant, bon c'est vrai que parfois des typedefs pourraient améliorer la lisibilité du code ;
    • le code des bibliothèques standards ne sont pas toujours exemples de lisibilité avec des #define de partout.


    De plus, de l'utilisation que je fais de la bibliothèque standard, je ne trouve pas cela paradoxale dans le cadre où un vector<int> n'est pas un vector<float> et sont donc bien deux "classes différentes".


    par contre si tu me mets un héritage et des fonctions virtuelles, tu me "forces" à payer le coût (bien que faible j'en convient).
    Je vois cela comme de l'optimisation prématurée et je préfère mettre l'accent sur la lisibilité et l'évolutivité du code.
    De plus, ModuleManagerImpl doit obligatoirement hériter d'une interface IModuleManager pour des contraintes qui sont miennes (certains utilisateurs ne sont pas dans le même fichier binaire que l'implémentation de ModuleManager).

    Je ne force donc rien car il est toujours possible d'implémenter soit-même son propre ModuleManager.

    L'introduction d'une classe de base c'est un type erasure intrusif, et comme tout ce qui est intrusif, ce n'est à introduire que si c'est nécessaire.
    Je ne comprend pas pourquoi il est intrusif.

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

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