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

Boost C++ Discussion :

[type_traits]Utilisation d'une surcharge par paramètres


Sujet :

Boost C++

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2007
    Messages
    39
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 39
    Points : 21
    Points
    21
    Par défaut [type_traits]Utilisation d'une surcharge par paramètres
    Bonjour à tous,

    J'ai vu que beaucoup de classes de type_traits suivaient l'idée d'une classe A héritant de B, et de spécialisations de classe A héritant de C (B et C étant boost::true_type et boost::false_type, et A étant par exemple is_integer).
    L'idée étant de définir, dans une fonction générique FONC<T>, un appel à la fonction CALL( A<T> ), qui invoque l'une des deux surcharges CALL( B ) ou CALL( C ) selon la valeur de T. On trouve un exemple ici.
    Pour clarifier, j'ai le sentiment que l'idée générale est de faire :

    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
    #include	<iostream>
     
    struct True {};
    struct False {};
     
    template <class T> struct IsInt : public False {};
    template <> struct IsInt <int> : public True {};
     
    void Fonc( False const& )
    { std::cout << "Is not signed int\n"; }		
    void Fonc( True const& )
    { std::cout << "Is signed int\n"; }		
     
    template <class T>
    void Do( void )
    { Fonc( IsInt<T>() ); }
    Ma question est : je suis géné par le fait d'utiliser une surcharge "classique" par paramètre, où une spécialisation de template suffirait. Il suffirait alors de définir A::type (comme le fait add_const, etc.) en fonction du template de A, et d'invoquer CALL<A::type>() au lieu de CALL( A::type() ). Pour être plus clair, cela voudrait dire faire :
    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
    #include	<iostream>
     
    struct True {};
    struct False {};
     
    template <class T> struct IsInt
    { typedef False type; };
    template <> struct IsInt <int> 
    { typedef True type; };
     
    template <class T>
    void Fonc( void )
    { std::cout << "Is not signed int\n"; }		
    template <>
    void Fonc<True>( void )
    { std::cout << "Is signed int\n"; }		
     
    template <class T>
    void Do( void )
    { Fonc<typename IsInt<T>::type>(); }
    Cela me semble plus correct, et ça m'a l'air aussi plus performant : j'ai regardé les instructions ASM générées par g++ -O2, et le premier bout de code passe un argument à Fonc(), même si cet argument n'est pas utilisé (-> il rajoute une instruction "lea ...").
    Mais j'aimerais avoir votre idée sur la question :
    - qu'en pensez-vous ?
    - est-il possible d'éviter une surcharge paramétrique dans un tel cas avec boost::is_integer ou struct similaire ?

    Merci beaucoup

    Edit : je viens de penser à cette autre solution :
    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
    #include	<iostream>
    #include	<boost/type_traits.hpp>
     
    template <class T> struct IsInt : public boost::false_type {};
    template <> struct IsInt <int> : public boost::true_type {};
     
    template <bool>
    void Fonc( void )
    { std::cout << "Is not signed int\n"; }		
    template <>
    void Fonc<true>( void )
    { std::cout << "Is signed int\n"; }		
     
    template <class T>
    void Do( void )
    { Fonc<IsInt<T>::value>(); }

  2. #2
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Historiquement, certains compilateurs ont mis du temps à avoir un implémentation correcte de la spécialisation de fonctions template. Le passage via une surcharge élimine de problème.

    C'est une des raisons que je vois à ce code (ce n'est peut être pas la seule).
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2007
    Messages
    39
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 39
    Points : 21
    Points
    21
    Par défaut
    Salut,

    Si mon code cible seulement des compilateurs implémentant correctement la full specialization de fonction template, me conseilles-tu d'utiliser cette dernière (-> le 3ème bout de code) plutôt que la méthode par surcharge paramétrique proposée dans la doc de Boost (-> le 1er bout de code) ?

  4. #4
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par ehmicky
    j'ai regardé les instructions ASM générées par g++ -O2, et le premier bout de code passe un argument à Fonc(), même si cet argument n'est pas utilisé (-> il rajoute une instruction "lea ...").
    Je soupçonne que tu te sois trompé quelque part. Ça me semble impossible que le code généré soit différent avec n'importe quel compilateur moderne et des options similaires à 02.

    Par exemple, que ce soit par spécialisation ou par surcharge, avec visual studio 2010 en release, le code suivant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main()
    {
       Do<int>();
    }
    est compilé exactement de la même manière que celui-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    int main()
    {
      std::cout << "Is signed int\n";
    }
    Edit : Une petite remarque pour la version avec surcharge: Dans les implémentations de la STL ou encore dans boost, les structures vides servant de tag sont systématiquement passés par valeur. Il me semble que c'est du au fait que ce soit de toute façon plus court à taper mais aussi que l'optimiseur est probablement un peu plus performant avec des passages par valeur, vu qu'il n'y a aucune problématique d'aliasing.
    Peut être devrait tu recommencer le test en remplaçant ce genre de code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    void Fonc( False const& )
    { std::cout << "Is not signed int\n"; }
    par celui-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    void Fonc( False )
    { std::cout << "Is not signed int\n"; }

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2007
    Messages
    39
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 39
    Points : 21
    Points
    21
    Par défaut
    Salut,

    Effectivement, main() est optimisé de telle manière qu'il appelle directement Fonc(), et Do() est viré, mais c'est à condition que Do() soit assez simple pour être viré. Là en l'occurence, c'est le cas parce que c'était pour alléger l'exemple.
    Pour avoir un cas "réel" et simuler un Do() plus complexe, je peux rajouter -fno-inline (mais rajouter une dizaine de lignes dans Do() a le même effet) :
    Par exemple, pour un fichier c.cpp :
    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>
     
    struct True {};
    struct False {};
     
    template <class T> struct IsInt : public False {};
    template <> struct IsInt <int> : public True {};
     
    void Fonc( False const& )
    { std::cout << "Is not signed int\n"; }		
    void Fonc( True const& )
    { std::cout << "Is signed int\n"; }		
     
    template <class T>
    void Do( void )
    { Fonc( IsInt<T>() ); }
     
    int main(void)
    {
    	Do<int>();
    	return 0;	
    }
    et un fichier d.cpp :
    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
    #include	<iostream>
     
    template <class T> struct IsInt
    { static bool const value = false; };
    template <> struct IsInt <int>
    { static bool const value = true; };
     
    template <bool>
    void Fonc( void )
    { std::cout << "Is not signed int\n"; }		
    template <>
    void Fonc<true>( void )
    { std::cout << "Is signed int\n"; }		
     
    template <class T>
    void Do( void )
    { Fonc<IsInt<T>::value>(); }
     
    int main(void)
    {
    	Do<int>();
    	return 0;	
    }
    On obtient :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    $ g++ -Wall -Wextra -O2 -fno-inline -ggdb3 c.cpp && gdb -ex "disas Do<int>" -ex "quit" a.out | tail -n+2 | head -n-1
    Dump of assembler code for function Do<int>():
       0x0000000000400740 <+0>:	sub    rsp,0x18
       0x0000000000400744 <+4>:	lea    rdi,[rsp+0xf]
       0x0000000000400749 <+9>:	call   0x400730 <Fonc(True const&)>
       0x000000000040074e <+14>:	add    rsp,0x18
       0x0000000000400752 <+18>:	ret
    Utiliser un argument non-const& produit le même code, avec seulement la différence du passage par valeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Dump of assembler code for function Do<int>():
       0x0000000000400740 <+0>:	sub    rsp,0x18
       0x0000000000400744 <+4>:	mov    BYTE PTR [rsp],0x0
       0x0000000000400748 <+8>:	call   0x400730 <Fonc(True)>
       0x000000000040074d <+13>:	add    rsp,0x18
       0x0000000000400751 <+17>:	ret
    Que ce soit par valeur ou par référence, l'argument à Fonc est passé, alors que Fonc ne l'utilise pas.
    Cependant, avec la version sans surcharge paramétrique, on a simplement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $ g++ -Wall -Wextra -O2 -fno-inline -ggdb3 d.cpp && gdb -ex "disas Do<int>" -ex "quit" a.out | tail -n+2 | head -n-1
    Dump of assembler code for function Do<int>():
       0x0000000000400730 <+0>:	jmp    0x400720 <Fonc<true>()>
    (le code généré pour main et Fonc() demeure quant à lui le même)
    C'est peut-être spécifique à mon OS + compilo (g++ 4.6, Ubuntu 11.10 x86_64) par contre.

Discussions similaires

  1. Réponses: 6
    Dernier message: 13/10/2014, 10h22
  2. Réponses: 5
    Dernier message: 07/09/2011, 11h31
  3. Réponses: 1
    Dernier message: 23/03/2009, 13h48
  4. Réponses: 9
    Dernier message: 06/08/2008, 17h50
  5. [TSQL] variable à utiliser pour une liste de paramètre
    Par pemathez dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 28/03/2008, 09h34

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