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 :

Template VS typeid


Sujet :

C++

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Novembre 2011
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2011
    Messages : 14
    Points : 7
    Points
    7
    Par défaut Template VS typeid
    Bonjour mes amis,
    Que pensez-vous de ce code ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    template <class T>
    void maFonction (T a) {
      une instruction avec a;
      une autre instruction avec a;
      if (typeid(a).name() == "une certain type") une instruction;
      elseif (typeid(a).name() == "un autre type") une autre instruction;
      else instruction par défaut
    }
    Ce type de fonction permet, au sein d'une fonction générique, d'effectuer un certain traitement générique (lignes 3,4) et un certain traitement particulier selon le type de l'argument(lignes 5-6)

    En ce qui a trait à la philosophie POO de C++, peut-on considérer que cette fonction est bien générique ??

  2. #2
    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,

    Non, cette fonction ne peut pas etre considérée comme générique, parce qu'une partie qui, justement, va dépendre d'un type particulier

    La solution va passer par la factorisation de ton code...

    Cela peut, au choix, se faire de manière non OO par la surcharge de fonction, en gardant le meme nombre d'arguments
    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
     
    /* la version adaptée pour le type UnType */
    void subFunction(UnType /* const */ & t)
    {
        /* ce qu'il faut faire */
    }
    /* la version adaptée pour le type UnAutreType */
    void subFunction(UnAutreType /* const */ & t)
    {
        /* ce qu'il faut faire */
    }
    /* la version adaptée pour un int */
    void subFunction(int i)
    {
        /* ce qu'il faut faire */
    }
    /* et meme, pourquoi pas, une version générique, pour tous
     * les autres types possibles ;)
     */
    template <typename T>
    void subFunction(T /* const */ & t)
    {
        /* ce qu'il faut faire */
    }
     
    template <class T>
    void maFonction (T a) {
      une instruction avec a;
      une autre instruction avec a;
      subFunction(a);
    }
    soit (dans un mode plus OO) en passant par une classe template totalement ou partiellement spécialisée pour le(s) types qui doivent réagir différemment.

    Tu auras énormément d'avantages à travailler de la sorte:
    1. Tu libère ta fonction de la responsabilité de vérifier le type réelle de T (et, partant, tu en limite les dépendances envers les autres types)
    2. Tu évites d'avoir recours au RTTI (RunTime Type Information) qui est souvent gourmand en temps d'exécution
    3. Le type réel de T étant déterminé à la compilation, tu obtiens au final une fonction particulièrement rapide, ne serait-ce que parce que tu évite ta série de if ... else if...else
    4. Tu respecte pleinement OCP (Open Close Principle) parce que, si tu crées un nouveau type de donnée qui nécessite un traitement particulier, il te suffira de surcharger SubFunction (selon mon exemple) pour que tout fonctionne correctement avec le nouveau type
    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

  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
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Citation Envoyé par SiSharp Voir le message
    Bonjour mes amis,
    Que pensez-vous de ce code ?


    Citation Envoyé par SiSharp Voir le message
    Ce type de fonction permet, au sein d'une fonction générique, d'effectuer un certain traitement générique (lignes 3,4) et un certain traitement particulier selon le type de l'argument(lignes 5-6)
    typeid(a).name() est dépendant de l'implémentation de ton compilateur. Donc tu ne peux garantir (et c'est même le cas), que 2 compilateurs aient le même nommage pour les types. Plus, rien ne t'assure que d'une version sur l'autre, ou selon des options de compilations, ou même d'une compilation sur l'autre, le compilateur ne change pas le nom généré. Donc aucune portabilité et aucune garantie de pérennité.

    Si tu veux du polymorphisme, utilises les mécanismes adéquat du langage. N'essaies pas d'en bidouiller un nouveau à la main.


    Citation Envoyé par SiSharp Voir le message
    En ce qui a trait à la philosophie POO de C++, peut-on considérer que cette fonction est bien générique ??
    POO et généricité sont deux notions orthogonales.

  4. #4
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Novembre 2011
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2011
    Messages : 14
    Points : 7
    Points
    7
    Par défaut
    Merci pour vos réponses,

    koala01 : J'aime bien votre suggestion de factoriser ma fonction avec une toute petite fonction surchargée appelée à partir de la fonction générique. Du coup, la généricité est préservée élégamment.

    Ceci dit, LA solution aurait été de réécrire mes classes sous formes de templates, spécialisés au besoin. Mais bon, avec un projet d'environ 30 classes et contenant des milliers de lignes de code, je crois que je vais utiliser la manière simple cette fois-ci et tirer une leçon d'architecture pour la prochaine fois

    3DArchi : Je n'ai pas encore assez d'expérience en POO pour affirmer moi-même qu'elle est orthogonale à la généricité mais j'ai cependant appris avec cette expérience que la généricité est totalement incompatible avec l'encapsulation pure et dure. En effet, toutes mes classes sont hyper-encapsulées, ce qui m'empêche ici d'accèder au moindre membre sans avoir à passer par des accesseurs spécialiés... incompatibles avec la généricité !! La prochaine fois je vais "templatiser" mes classes

  5. #5
    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 SiSharp Voir le message
    3DArchi : Je n'ai pas encore assez d'expérience en POO pour affirmer moi-même qu'elle est orthogonale à la généricité mais j'ai cependant appris avec cette expérience que la généricité est totalement incompatible avec l'encapsulation pure et dure. En effet, toutes mes classes sont hyper-encapsulées, ce qui m'empêche ici d'accèder au moindre membre sans avoir à passer par des accesseurs spécialiés... incompatibles avec la généricité !! La prochaine fois je vais "templatiser" mes classes
    FAUX, ARCHI FAUX

    Commençons, si tu veux bien par t'expliquer les raisons de cette réaction

    Déjà, on peut faire de l'encapsulation avec un langage non OO, pour autant qu'il supporte le concept de structures de données (il est tout à fait possible de faire de l'encapsulation en C... la meilleure preuve en est le fameux FILE qui représente une structure dont on ne sait strictement rien )

    L'idée de l'Orienté Objets n'est pas l'encapsulation, mais plutôt de répondre à un besoin de substituabilité (par exemple, le fait de pouvoir faire passer un carré, un rectangle ou un cercle pour une forme "quelconque" et de pouvoir travailler avec cette forme)

    Le moyen d'arriver à obtenir cette substituabilité est de réfléchir non plus sur base des données utilisés, mais sur base des services que l'on peut attendre d'un type particulier: on se fout pas mal des 1527 pièces qui composent un moteur à explosion, par contre, on estimes que l'on est en droit d'attendre un certain nombre de services de la part du moteur (ex: démarrer, stoper, accélérer, transmettre le mouvement, que sais-je ) puis on va regarder quelles pièces sont nécessaires pour obtenir ces différents services.

    Enfin, le paradigme générique se base sur le principe que l'on ne sait pas quel type de donnée on va effectivement manipuler, par contre, on sait comment on va manipuler ces données

    Il est vrai que, étant donné que l'on va essentiellement réfléchir à la manière dont on va manipuler les données, cela nous force à réfléchir à "l'interface" proposée par celle-ci, et que cela se marie très bien avec l'idée qui sous tend au paradigme Objet de service rendu, mais rien ne nous empêche de faire du générique sans utiliser le paradigme objet mais en utilisant le paradigme procédural (on l'a bien vu avec l'exemple de code que j'ai donné plus haut )

    C'est pour cela que 3DArchi te dis que le paradigme OO et le paradigme générique sont orthogonaux : Tu peux très bien utiliser l'un et pas l'autre (ou l'autre et pas l'un) sans que cela ne te pose de problème majeur, mais il est vrai que, si tu prend les deux en même temps, alors, là, tu t'ouvre des horizons tout à fait nouveaux et insoupçonnés

    Une autre raison c'est le recours à outrance aux accesseurs est souvent le symptôme d'un problème de conception, du au non respect de ce que l'on appelle "la loi demeter".

    Il existe en effet une loi dite "demeter" qui nous explique que si A utilise B et que B utilise C, A ne devrait en aucun cas se trouver devant l'obligation de connaitre C pour utiliser B.

    Je reviens avec mon exemple de voiture...

    Tu te doute qu'une voiture a un moteur et un réservoir de carburant (il vaut mieux, si elle veut avancer )

    Tu sais très bien que ces "pièces" fournissent un certain nombre de services qui leur sont propres

    Mais, si tu n'es pas mécanicien, il y a neuf chances sur dix pour que tu n'aies jamais à t'intéresser au fonctionnement du moteur ou à l'emplacement du réservoir...

    Il y a d'ailleurs neuf chances sur dix pour que tu ne voies jamais un réservoir à carburant de ta vie!!!

    Mais la voiture va offrir un ensemble de service qui manipuleront le moteur et/ou le réservoir de manière transparente:

    En tant qu'utilisateur de la classe Voiture, tu n'as, a priori, aucun besoin de connaitre la classe Moteur ou la classe Reservoir

    Tu ne devrais donc pas te retrouver face à ne serait ce que la possibilité de faire voiture.getReservoir().fill() ( et surtout pas sous cette forme, vu qu'un accesseur devrait toujours renvoyer un objet constant ) ni voiture.getReservoir().getMaxQuantity() par contre, tu fera sans doute quelque chose comme voiture.addCarburant(quantity) qui aura pour effet de... rajouter quantity au réservoir ou voiture.tankState()qui aura pour effet de renvoyer le rapport entre la quantité de carburant qu'il reste dans le réservoir et la capacité maximale de celui-ci

    Mais bon, quoi qu'il en soit, il est vrai qu'il faut trouver un moyen simple de traiter les différents types, dont certains dépendent peut etre de paramètres template fournis, et, tant qu'à faire, qui permettent à une classe de récupérer ces types sans se poser *trop* de question

    Les choses sont quand même bien faites en ce bas monde, parce qu'il existe un mot clé qui fait exactement ce genre de chose : typedef

    Cela demande un peu de réflexion pour y arriver, mais, une fois que tu penses à utiliser typedef le principe est cependant simplissime : chaque fois qu'il te faut un type particulier différent de ceux qui existent déjà, tu crées un typedef et tu lui donne un nom que tu essayera de réutiliser chaque fois que tu voudras manipuler un type correspondant...

    Je suis, justement, sur un code qui pourrait servir d'exemple génial pour cela...

    Je veux pouvoir représenter des coordonnées (des "points") sur des systèmes deux dimensions (axe des X et axe des Y) et trois dimensions (axe des X, axe des Y et axe des Z) avec n'importe quelle valeur pouvant être considérée comme numérique (que ce soit des int ou des long long, des float ou des long double... et, ma fois, pourquoi pas des std::complex )

    J'ai donc commencé par créer des structures vide qui servent à déterminer si je travaille dans un système de coordonnées 2D ou 3D:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    struct Point2DFlag{};
    struct Point3DFlag{};
    Et j'ai créé deux spécialisations partielles pour ma classe point dans lesquelles j'ai défini les typedefs suivant:
    value_type : le type de la valeur utilisée pour représenter la position du point sur les axes
    flag_type : le drapeau utilisé pour savoir si on travaille dans un système 2D ou 3D
    point_type : le type de point que l'on est occupé à manipuler
    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
    template <typename Type, typename Flag>
    class Point;
    template <typename Type>
    class Point<Type, Point2DFlag>
    {
        public:
            typedef Type value_type;
            typedef Point2DFlag flag_type;
            typedef Point<value_type, flag_type> point_type;
            /* je fais grace du reste ;) */
    };
    class Point<Type, Point3DFlag>
    {
        public:
            typedef Type value_type;
            typedef Point3DFlag flag_type;
            typedef Point<value_type, flag_type> point_type;
            /* je fais grace du reste ;) */
    };
    Grace à cela, si je dispose d'une fonction getX() qui renvoie la valeur de la position du point sur l'axe des X, je n'ai meme pas besoin de savoir si c'est un int, un char ou un long double...

    Je n'ai quà utiliser
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monPointParticulier::value_type value = monPointParticulier.getX()
    (la nouvelle norme fournit maintenant le mot clé auto, mais cela fonctionne déjà avec n'importe quel compilateur respectant la norme de 2003 )
    Puis, j'ai voulu créer un bipoint... j'ai travaillé de la meme manière:
    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
    template <typename Type, typename Flag>
    class Bipoint
    {
        public:
            typedef Type value_type;
            typedef Flag flag_type; 
            //Ben oui, j'ai besoin... de points pour réger une paire de point :D
            typedef Point<value_type, flag_type> point_type;
            typedef Bipoint<value_type, flag_type> bipoint_type;
            /* je le donne uniquement parce qu'il va servir ma cause :D */
            Bipoint(point_type const & first = point_type(), 
                    point_type const & second = point_type()):
                    first_(first), second_(second)
        {
        }
    /* je passe le reste ;) */
    };
    Et là, j'ai été confronté à un problème...

    En effet, je voulais que mon bipoint soit automatiquement "normalisé", c'est à dire que first_ prenne les valeurs minimales et que second_ prenne d'office les valeurs maximales

    L'idée était, pour pour chaque dimension, d'utiliser std::min(dimension_du_premier, dimension_du_deuxieme) pour first_ et std::max(dimension_du_premier, dimension_du_deuxieme) pour second_

    Or, les points 2D travaillent avec x et y, alors que les points 3D travaillent sur x, y... et z

    Sans passer par quelque chose d'intermédiaire, le problème était irrésovable : soit ne n'utilisais que les accesseurs sur x et y, ce qui fonctionnait avec des points 2D mais z n'était pas normalisé pour les point 3D, soit j'utilisais également l'accesseur sur z, qui fonctionne avec les points 3D mais... n'existe pas avec les points 2D

    J'ai donc créé une nouvelle classe template "Normalizer" partiellement spécialisée en fonction du flag 2D/3D, sous la forme de
    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
    template <typename Type, typename Flag>
    class Normalize<Type, Point2DFlag>
    {
        public:
            typedef Type value_type;
            typedef Point2DFlag flag_type; // inutile ici, mais, comme je l'ai :D
            typedef Point<value_type, flag_type> point_type;
            void operator()(point_type & first, point_type &second)
            {
                 first = point_type(std::min(first.x(), second.x()),
                                     std::min(first.y(), second.y()));
     
                 second = point_type(std::max(first.x(), second.x()),
                                     std::max(first.y(), second.y()));
            }
    };
    /* je passe sur la spécialisation partielle avec Point3DFlag, mais
     * elle prend utilse aussi z() ;)
     */
    j'ai rajouté un typedef sur Normalizer à la class Bipoint typedef Normalizer<value_type, flag_type> normalizer_type; et j'ai modifié le constructeur de bipoint sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
            Bipoint(point_type const & first = point_type(), 
                    point_type const & second = point_type()):
                    first_(first), second_(second)
        {
            normalizer_type()(first_,second_);
        }
    Et mon bi point fonctionne maintenant aussi bien en 2D qu'en 3D

    Je dois cependant avouer (pour etre quand meme honnête ) que j'ai un volontairement décidé de contourner demeter en fournissant un accesseur sur first_ et sur second_, car, autrement, j'aurais du spécialiser partiellement mon bi point de manière à ce que sa version 2D ne propose que l'acces à x et à y pour les deux points et que la version 3D propose également l'accès à z pour ceux-ci...

    J'ai cependant (tout comme le point )décidé de ne pas fournir de mutateur sur first_ et sur second_ car le bipoint a sémantique de valeur : si je modifie ne serait-ce qu'un seul des points qui le compose, j'obtiens... un autre bipoint

    L'avantage, c'est que le point et le bipoint sont "tellement bas" dans tout ce qui va suivre (et dont je ne parlerai pas, je ne vais quand meme pas refaire tout le projet ici ) que je peux réellement considérer qu'ils font partie des primitives de mon projet

    Ensuite, j'ai voulu créer une classe qui gère les contraintes de taille des objets qui tiennent dans un bi point...

    Il s'agit d'une classe qui est en mesure:
    • de s'assurer qu'une taille est comprise dans une fourchette donnée
    • de modifier la fourchette au niveau du minimum
    • de modifier la fourcher au niveau du maximum
    • de modifier la taille et
    • de lancer une exception si une modification fait que l'on se retrouve hors limite (CAD aussi bien si on essaye de régler la taille minimale à 10 avec une taille à 9 que si on essaye de régler la taille à 126 avec une taille maximale à 125 )
    Et, bien sur (ou aurait été le plaisir ) je voulais qu'un type d'exception correspondant à la contrainte réellement violée soit correctement lancée si le besoin en était: C'est à dire que je voulais qu'une exception différente soit lancée si je violais la contrainte de largeur minimale de si je violais la contrainte de hauteur minimale Et là, j'avais de nouveau le problème qu'il y avait toute une dimension (la profondeur ) qui existe en 3D et qui 'existe pas en 2D...

    Et, tu t'en seras douté, je n'avais pas du tout envie de faire une spécialisation partielle de la classe, que je voulais, au demeurant, garder aussi simple que possible

    La solution est flagrante dans la théorie : il fallait que je ne travaille que sur une dimension à chaque fois

    Mais si, avec ce que j'ai jusqu'à présent, je sais faire la distinction entre un point 2D et un point 3D, je ne dispose pas de la possibilité de faire la distinction entre l'axe des X, l'axe des Y ou l'axe des Z

    J'ai donc créé trois autres structures vides qui me serviront de flags:
    • XAxisFlag qui indique que l'on travaille sur l'axe des X
    • YAxisFlag qui indique que l'on travaille sur l'axe des Y et
    • ZAxisFlag qui indique que l'on travaille sur l'axe des Z
    Puis j'ai commencé à coder ma classe:
    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
    template <typename Type, typename Flag, typename AxisFlag, 
              typename Decorator>
    class SizeConstraintImpl
    {
        public:
            typedef Type value_type;
            typedef Point2DFlag flag_type; 
            typedef AxisFlag axis_flag;
            //Ben oui, j'ai besoin... de points pour réger une paire de point :D
            typedef Point<value_type, flag_type> point_type;
            typedef Bipoint<value_type, flag_type> bipoint_type;
            static void checkConstraint(value_type min, 
                                             value_type max, 
                                             value_type toTest)
            {
                 checkMinConstraint(min, toTest);
                 checkMaxConstraint(max, toTest);
            }
            static void checkMinConstraint(min, toTest)
            {
                if(min > toTest)
                    throw // ... hé merde!!!...
            }
    };
    Oui, merde!!! Je fais comment moi pour m'assurer que la bonne exception (celle qui correspond exactement au contexte) soit lancée

    En plus, j'aurai le meme problème avec checkMaxConstraint !

    Bon, ben, on va faire une petite classe en plus, dont la spécialization permettra de déterminer la (ou les ) exceptions qui correspondent à la dimension que l'on utilise
    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
    33
    34
    35
    36
    37
    38
    39
    40
     
    template <typename Flag>
    struct SizeConstraintExceptionSender;
     
    template<>
    struct SizeConstraintExceptionSender<XAxisFlag>
    {
         void throwMin() const
        {
            throw MinWidthConstraintsException();
        }
        void throwMax() const
        {
            throw MaxWidthConstraintsException();
        }
    };
    template<>
    struct SizeConstraintExceptionSender<YAxisFlag>
    {
         void throwMin() const
        {
            throw MinHeightConstraintsException();
        }
        void throwMax() const
        {
            throw MaxHeightConstraintsException();
        }
    };
    template<>
    struct SizeConstraintExceptionSender<ZAxisFlag>
    {
         void throwMin() const
        {
            throw MinDeepConstraintsException();
        }
        void throwMax() const
        {
            throw MaxDeepConstraintsException();
        }
    };
    Maintenant, on rajoute le typedef qui va bien à ma classe : typedef TSizeConstraintExceptionSender<axis_flag> exceptionSender;Aaahhh, je peux continuer
    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
     
            static void checkMinConstraint(min, toTest)
            {
                if(min > toTest)
                    throw exceptionSender_type().throwMin();
            }
            static void checkMaxConstraint(max, toTest)
            {
                if(max < toTest)
                    throw exceptionSender_type().throwMax();
            }
            void setMinSize(value_type const & value)
            {
                checkMinConstraint(value,/* ... Re merde!!! ...*/);
            }
    Il faut que je puisse calculer la taille de la dimension qui correspond !

    Bon, ben, on va créer encore une politique... y a pas le choix
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
        template <typename Type, typename Flag, typename AxisFlag>
        struct BipointSizeCalculator;
        template <typename Type, typename Flag>
        struct BipointSizeCalculator<Type,Flag,XAxisFlag>
        {
            typedef TPoint<Type, Flag> point_type;
            typedef TBiPoint<Type, Flag> bipoint_type;
            /* je sais (parce que je l'ai décidé :D ) que j'obtiendrai
             * assez facilement un bipoint, mais il me faut les positions des deux
             * point sur l'axe concerné (laxe des x, ici)...
             * je crée donc un opérateur() par étape logique permettant de
             * passer d'un bipoint à ces deux valeurs :D
             */
            Type operator() (Type x1, Type x2) const
            {
                return abs(x1 - x2);
            }
            Type operator()(point_type const & first, point_type const & second) const
            {
                return this->operator()(first.x(),second.x());
            }
            Type operator()(bipoint_type const & bipoint) const
            {
                return this->operator()(bipoint.first(),bipoint.second());
            }
        };
        template <typename Type, typename Flag>
        struct BipointSizeCalculator<Type,Flag,YAxisFlag>
        {
            typedef TPoint<Type, Flag> point_type;
            typedef TBiPoint<Type, Flag> bipoint_type;
            Type operator() (Type y1, Type y2) const
            {
                return abs(y1 - y2);
            }
            Type operator()(point_type const & first, point_type const & second) const
            {
                return this->operator()(first.y(),second.y());
            }
            Type operator()(bipoint_type const & bipoint) const
            {
                return this->operator()(bipoint.first(),bipoint.second());
            }
        };
        template <typename Type, typename Flag>
        struct BipointSizeCalculator<Type,Flag,ZAxisFlag>
        {
            Type operator() (Type z1, Type z2) const
            {
                return abs(z1 - z2);
            }
            Type operator()(point_type const & first, point_type const & second) const
            {
                return this->operator()(first.z(),second.z());
            }
            Type operator()(bipoint_type const & bipoint) const
            {
                return this->operator()(bipoint.first(),bipoint.second());
            }
        };
    Bon, je peux continuer

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
            void setMinSize(value_type const & value)
            {
                checkMinConstraint(value,decorated().bipoint());
                minSize_ = value;
            }
            void setMaxSize(value_type const & value)
            {
                checkMaxConstraint(value,decorated().bipoint());
                maxSize_ = value;
            }
    Je vais m'arrêter là, parce que sinon, tu vas t'endormir devant tout ce code

    Mais tu auras remarqué que, si tu choisi correctement tes arguments template, que tu arrives à déterminer les bons aspects orthogonaux pour définir les politiques qui vont bien, et que tu joue correctement avec les typedef (j'aurais pu décider de travailler avec l'héritage privé, mais cela n'aurait pas changé grand chose, et le code aurait sans doute été bien moins lisible ) il devient particulièrement facile de ne travailler finalement qu'avec les typedefs que tu auras définis
    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

  6. #6
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 620
    Points
    15 620
    Par défaut
    Magnifique illustration de la démarche "méta programmation" et du principe "all problems in computer science can be solved by another level of indirection" (David Wheeler).

    A lire, relire et mettre dans la FAQ

  7. #7
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par koala01 Voir le message
    L'idée de l'Orienté Objets n'est pas l'encapsulation, mais plutôt de répondre à un besoin de substituabilité (par exemple, le fait de pouvoir faire passer un carré, un rectangle ou un cercle pour une forme "quelconque" et de pouvoir travailler avec cette forme)
    J'ai l'impression que cette définition correspond plutôt au polymorphisme.

  8. #8
    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 oodini Voir le message
    J'ai l'impression que cette définition correspond plutôt au polymorphisme.
    En fait, le polymorphisme découle directement de la substituabilité

    Le polymorphisme, c'est le fait qu'un objet substitué ( "passant pour etre" du type de base) puisse avoir un comportement adapté à son type réel

    Si tu fait Base * b = new Derived; tu utilises la substituablilité, en utilisant un objet de type Derived comme étant un objet de type Base.

    Si, à la ligne suivante, tu fait b->doSomething(); et que doSomething() a un comportement adapté pour le type Derived, tu fais du polymorphisme

    Tu ne peux faire du polymorphisme d'inclusion que parce que tu as la possibilité de jouer avec la substituabilité

    La généricité est, effectivement, une autre forme de substituabilité, mais tu utilise alors le polymorphisme paramétrique, et la manière d'envisager les choses sera alors différente
    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

  9. #9
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par koala01 Voir le message
    En fait, le polymorphisme découle directement de la substituabilité
    Certes, mais la notion de polymorphisme n'est pas synonyme de la notion d'objet.

  10. #10
    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 oodini Voir le message
    Certes, mais la notion de polymorphisme n'est pas synonyme de la notion d'objet.
    Non, et c'est ce que j'ai bien expliqué sur la ligne juste après

    C'est la notion de substituabilité qui est la base du paradigme OO

    Ce qui fournit la notion d'objet, c'est le fait que l'on s'attend à ce qu'un objet fournisse un certain nombre de services, et la notion de polymorphisme est le fait qu'un objet substitué puisse rendre des services adaptés en fonction du type réel ("dynamique") de celui-ci
    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

  11. #11
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    J'ai cru comprendre que la programmation objet était apparue avec la première version de Simula, qui ne comportait pas de polymorphisme, il me semble.

  12. #12
    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 oodini Voir le message
    J'ai cru comprendre que la programmation objet était apparue avec la première version de Simula, qui ne comportait pas de polymorphisme, il me semble.
    Ce n'est pas exclu, parce que la notion d'objet s'intéresse en priorité aux services rendus par les objets (et donc par les types qui permettent de créer ces objets)

    Par contre, la notion d'objet sans substituabilité (et donc sans polymorphisme) reste un canard boiteux

    En effet, la notion d'objet sans substituabilité te facilite très certainement le respect d'une encapsulation stricte, mais nécessite un certain code bloat lorsqu'il s'agit, par exemple de faire se tracer un cercle et un rectangle, alors que tous deux fourniraient un service draw()
    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

Discussions similaires

  1. [Templates] Quel système utilisez-vous ? Pourquoi ?
    Par narmataru dans le forum Bibliothèques et frameworks
    Réponses: 270
    Dernier message: 26/03/2011, 00h15
  2. exception en template avec des typeid
    Par Julien_C++ dans le forum Langage
    Réponses: 8
    Dernier message: 03/01/2007, 13h55
  3. appliquer plusieurs templates
    Par Manu_Just dans le forum XSL/XSLT/XPATH
    Réponses: 7
    Dernier message: 04/04/2003, 16h26
  4. template match="node() mais pas text()"
    Par Manu_Just dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 26/03/2003, 10h52
  5. [XSLT] template
    Par demo dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 09/09/2002, 11h31

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