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

C++ Discussion :

Class de traits ou préprocessor ?


Sujet :

C++

  1. #1
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut Class de traits ou préprocessor ?
    Bonjour

    Une question à propos de bonnes pratiques... m'est avis que je vais récolter des deux avis . Voici le problème: j'utilise des smarts pointers dans un projet, ceux de boost. Or, il se trouve que je trouve chiant d'écrire à chaque fois:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    boost::shared_ptr<MaClasse> variable etc bidule truc;
    Parce que je trouve ça long, d'une part, et parce que je veux me réserver la possibilité (même si la probabilité est très faible) de ne pas être dépendant de boost et de pouvoir changer de smart pointer un jour si je veux. Alors j'écris dans mon header :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    typedef boost::scoped_ptr<MaClasse> MaClasse_local;
    typedef boost::shared_ptr<MaClasse> MaClasse_var;
    typedef boost::weak_ptr<MaClasse> MaClasse_wref;
    Et j'utilise MaClasse_var (par exemple) dans mon code. Comme je suis paresseux, ce qui est une qualité pour nous autres, informaticiens, j'ai écrit la macro DeclarePointers(MaClasse) (le prénom a été changé) qui définit les typedefs précédents.

    Il existe une seconde solution: écrire une classe de traits, que j'utiliserais par exemple de la manière suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SmartPrt<MaClasse>::local // code ...
    SmartPrt<MaClasse>::var // code ...
    SmartPrt<MaClasse>::wref // code ...
    Ce qui reste un peu plus chiant à écrire mais économise une macro et a le mérite de ne compiler que les templates effectivement utilisés dans le code.


    Selon vous, quel serait la meilleure solution ? Pourquoi ?
    Find me on github

  2. #2
    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
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,

    Avec C++0x et les alias de template, tu pourras faire quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template<class T> using sharedPtr = boost::shared_ptr<T>;
     
    //...
    sharedPtr<monType> ptr;
    En attendant, F.A.Q : Peut-on créer un alias (typedef) sur des templates ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template<typename T>
    struct smart_ptr
    {
    typedef boost::shared_ptr<T> shared_ptr;
    };
    Ensuite, l'utilisation smart_ptr<TYPE>::shared_ptr devient indépendante de boost :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    smart_ptr<MON_TYPE>::shared_ptr var;
    Là, le typedef peut être utile :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef smart_ptr<MON_TYPE>::shared_ptr shptr_MonType;
    Pour ce dernier, une macro ou directement, c'est pareil à mon avis.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut
    Citation Envoyé par jblecanard Voir le message
    Bonjour

    Une question à propos de bonnes pratiques... m'est avis que je vais récolter des deux avis . Voici le problème: j'utilise des smarts pointers dans un projet, ceux de boost. Or, il se trouve que je trouve chiant d'écrire à chaque fois:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    boost::shared_ptr<MaClasse> variable etc bidule truc;
    Parce que je trouve ça long,
    Mais bon, c'est quand meme pas la mort non plus :p
    d'une part, et parce que je veux me réserver la possibilité (même si la probabilité est très faible) de ne pas être dépendant de boost et de pouvoir changer de smart pointer un jour si je veux.
    C'est déjà une meilleure raison, mais... pourquoi

    Peut etre devrais tu déjà t'interroger sur la durée de vie de ton projet, car, si c'est un projet à court terme, les raisons que tu pourrais avoir de t'orienter vers une autre bibliothèque risquent de se faire rares
    Alors j'écris dans mon header :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    typedef boost::scoped_ptr<MaClasse> MaClasse_local;
    typedef boost::shared_ptr<MaClasse> MaClasse_var;
    typedef boost::weak_ptr<MaClasse> MaClasse_wref;
    C'est, à mon avis, ce qui pourrait nécessiter le moins de manipulation dans l'éventualité où tu souhaite passer à une autre bibliothèque... pour autant qu'à pointeur équivalent, tu aies une interface équivalente (ce qui n'est pas forcément certain )

    Et j'utilise MaClasse_var (par exemple) dans mon code. Comme je suis paresseux, ce qui est une qualité pour nous autres, informaticiens, j'ai écrit la macro DeclarePointers(MaClasse) (le prénom a été changé) qui définit les typedefs précédents.
    Est-ce vraiment plus facile je n'en suis pas persuadé...
    Il existe une seconde solution: écrire une classe de traits, que j'utiliserais par exemple de la manière suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SmartPrt<MaClasse>::local // code ...
    SmartPrt<MaClasse>::var // code ...
    SmartPrt<MaClasse>::wref // code ...
    La classe de traits présenterait sans doute aussi pas mal d'avantages, mais il faut les chercher plus du coté de la possibilité de choisir la politique de gestion des pointeurs que du coté de la facilité... après tout, tu ne gagnerait que l'écriture de boost::... est-ce que cela vaut réellement la peine
    Ce qui reste un peu plus chiant à écrire mais économise une macro et a le mérite de ne compiler que les templates effectivement utilisés dans le code.
    Il y a de grandes chances pour qu'il en aille de même pour les macros, étant donné que leur mode de fonctionnement est basé sur le remplacement d'une expression donnée par une autre

    Selon vous, quel serait la meilleure solution ? Pourquoi ?
    Si, vraiment, tu veux "économiser" quelques caractères, je dirais que les deux solutions font des choses différentes. Dans certaines situations les macro seront préférables, et, dans d'autres, ce serait la classe de trait qui présenterait le plus d'avantages, sans qu'il soit possible de déterminer "laquelle est meilleure que l'autre".

    Mais, encore une fois, en as-tu vraiment besoin

    Ne pourrais tu pas, tout simplement, te contenter d'une directive using
    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

  4. #4
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par koala01 Voir le message
    pour autant qu'à pointeur équivalent, tu aies une interface équivalente (ce qui n'est pas forcément certain )
    J'y ai pensé, mais pour des smart pointers, tu peux presque en être sûr à l'avance, les interfaces étant les constructeurs (sur T* et par copie) et l'opérateur d'affectation.

    Citation Envoyé par koala01 Voir le message
    Est-ce vraiment plus facile je n'en suis pas persuadé...
    Rien que sur un petit projet, je peux te dire que oui, c'est vraiment agréable ! Ne serais ce qu'au niveau de la lisibilité. L'économie de caractères, c'est juste pour mon confort en temps que dev. Et les usings, j'en utilise déjà parce que je bind sur plusieurs librairies, alors je préfère ne pas en abuser.

    Citation Envoyé par koala01 Voir le message
    Il y a de grandes chances pour qu'il en aille de même pour les macros, étant donné que leur mode de fonctionnement est basé sur le remplacement d'une expression donnée par une autre
    Certes oui. La question se soulève alors : est ce que un "typedef NomTemplate<Type> NomALaCon;" provoque à lui seul la compilation du template ?

    Ce sujet, c'est un peu du chipotage, mais je sais qu'il y en a sur ce forum qui aiment le chipotage
    Find me on github

  5. #5
    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
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Citation Envoyé par jblecanard Voir le message
    Certes oui. La question se soulève alors : est ce que un "typedef NomTemplate<Type> NomALaCon;" provoque à lui seul la compilation du template ?
    A mon sens, il ne s'agit pas d'une instanciation explicite ou implicite. Donc je dirais non. Mais il est vrai que pour les template, en général je préfère 'qualifier' le compilateur si ces problématiques d'instanciation sont importantes (code bloat essentiellement).

    @koala : ça a un intérêt de passer par une classe trait pour déterminer les smart pointeurs à utiliser. En terme de design, découpler 2 modules (surtout là où ils sont vraiment orthogonaux) n'est jamais inutile. De plus, même s'il est peu probable de changer de bibliothèque de smart ptr sur un projet, une partie du code peut faire l'objet de reuse dans un autre projet n'utilisant pas Boost. Ce découplage facilitera grandement cette réutilisation.

  6. #6
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Certes oui. La question se soulève alors : est ce que un "typedef NomTemplate<Type> NomALaCon;" provoque à lui seul la compilation du template ?
    Non..
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    J'y ai pensé, mais pour des smart pointers, tu peux presque en être sûr à l'avance, les interfaces étant les constructeurs (sur T* et par copie) et l'opérateur d'affectation.
    En théorie, effectivement...

    En pratique... va savorir
    Rien que sur un petit projet, je peux te dire que oui, c'est vraiment agréable ! Ne serais ce qu'au niveau de la lisibilité. L'économie de caractères, c'est juste pour mon confort en temps que dev. Et les usings, j'en utilise déjà parce que je bind sur plusieurs librairies, alors je préfère ne pas en abuser.
    Attention, je suis le premier à reconnaitre les bienfaits des macros (si elles sont utiles), des typedefs et des classes de traits, mais je persiste à dire que, si ta seule motivation est de remplacer boost::weak_ptr<UnType> par autre chose, fusse plus simple, n'est clairement pas suffisante
    Ce sujet, c'est un peu du chipotage, mais je sais qu'il y en a sur ce forum qui aiment le chipotage
    On peut aimer le chipotage, et on peut même envisager de le faire dans du code en exploitation.

    Mais tout est toujours une question d'utilité: chipoter pour chipoter est une très mauvaise chose, alors que le fait de chipoter pour éviter de se retrouver bloqué suite à de mauvaise décisions de conception au départ, cela peut avoir tout son sens
    Citation Envoyé par 3DArchi Voir le message
    @koala : ça a un intérêt de passer par une classe trait pour déterminer les smart pointeurs à utiliser. En terme de design, découpler 2 modules (surtout là où ils sont vraiment orthogonaux) n'est jamais inutile. De plus, même s'il est peu probable de changer de bibliothèque de smart ptr sur un projet, une partie du code peut faire l'objet de reuse dans un autre projet n'utilisant pas Boost. Ce découplage facilitera grandement cette réutilisation.
    Si tu relis mon intervention, tu remarqueras que je parle des avantages de la classe trait, et que je mets en avant la possibilité de choisir la politique de gestion des pointeurs

    Mais, de la manière dont jblecanard présente les choses, on pourrait croire qu'il s'oriente vers quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    template <typename T>
    struct smart_ptr
    {
        typedef boost::weak_ptr<T> weak;
        typedef boost::shared_ptr<T> shared;
        /* et tous les autres, dans la même structure */
    };
    et, là, tu remplace simplement l'écriture de boost::some_ptr<UnType> par smart_ptr<UnType>::some ... Du point de vue de l'écriture, cela ne change rien

    Par contre, s'il se dirige, effectivement, vers quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T, typename Policy
    struct smar_pointer
    {
        typedef typename Pölicy<T> ptr_type;
    };
    il en va évidemment tout autrement
    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

  8. #8
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Mais tout est toujours une question d'utilité: chipoter pour chipoter est une très mauvaise chose, alors que le fait de chipoter pour éviter de se retrouver bloqué suite à de mauvaise décisions de conception au départ, cela peut avoir tout son sens
    Complètement d'accord. Mon projet actuel n'a pas absolument besoin d'être bien géré sur ce point, mais je me suis posé la question pendant le dev et me suis dit que le débat pourrait m'être utile à terme.

    Citation Envoyé par koala01 Voir le message
    Par contre, s'il se dirige, effectivement, vers quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T, typename Policy
    struct smar_pointer
    {
        typedef typename Pölicy<T> ptr_type;
    };
    il en va évidemment tout autrement
    Effectiveme, ça se sert à rien sinon. J'avais un peu de mal avec les classes de traits, là j'ai parfaitement compris la valeur ajoutée potentielle
    Find me on github

Discussions similaires

  1. Classe comparable, traits
    Par Awakening dans le forum Langage
    Réponses: 2
    Dernier message: 21/12/2012, 09h27
  2. Réponses: 2
    Dernier message: 26/11/2010, 00h10
  3. Probleme avec une class qui traite la date
    Par tarikmahf dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 10/11/2008, 22h12
  4. classe de trait
    Par poukill dans le forum C++
    Réponses: 4
    Dernier message: 21/08/2007, 15h40
  5. classe de traits et conteneurs
    Par koopajah dans le forum C++
    Réponses: 6
    Dernier message: 02/02/2005, 10h58

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