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

Langage C++ Discussion :

Visibilité de template et SFINAE


Sujet :

Langage C++

  1. #1
    Membre averti Avatar de vikki
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    292
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mai 2007
    Messages : 292
    Points : 302
    Points
    302
    Par défaut Visibilité de template et SFINAE
    Bonjour à tous,

    Je ne suis pas vraiment sûr du lien entre l'intitulé du post et son sujet réel mais j'ai du mal à formuler tout ça et je ne suis pas un grand spécialiste de metaprogrammation. Quoi qu'il en soit, voici un exemple de code concernant ma question:

    Je reprend l'exemple wikki (un peu modifié) concernant la définition du SFINAE:


    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
    struct Test 
    {
        typedef int type;
    };
     
    template < typename T > 
    void f(typename T::type) {std::cout<<"def 1";} // definition #1
     
    template < typename T > 
    void f(T) {std::cout<<"def 2";}                // definition #2
     
    struct MaClasseDeTest
    {
         template < typename T >
         MaClasseDeTest(T param)
         {
               f<T>(5);
         }
    };
     
    int main()
    {
       MaClasseDeTest t1(10); // call #1 
     
       Test temp;
        MaClasseDeTest t2(temp);  // call #2 without error thanks to SFINAE
    }
    Maintenant je split ce code en deux header:


    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
     
    //header file1.h, contient la version 'de base' de f et l’implémentation de la classe MaClasseDeTest
     
    #include <iostream>
     
    template < typename T > 
    void f(T) {std::cout<<"def 2";}                // definition #2
     
    struct MaClasseDeTest
    {
         template < typename T >
         MaClasseDeTest(T unused)
         {
               f<T>(5);
         }
    };
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    //header file2.h, contient une autre version de f
    #include <iostream>
     
    struct Test 
    {
        typedef int type;
    };
     
    template < typename T > 
    void f(typename T::type) {std::cout<<"def 1";} // definition #1
    Le test:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    //main.cpp
     
    #include "file1.h"
    #include "file2.h"
     
     
    int main()
    {
       MaClasseDeTest t1(10); 
     
       Test temp;
        MaClasseDeTest t2(temp); 
    }
    Sous Visual 2010, le constructeur de MaClasseDeTest appelle bien les deux implémentations différentes de la fonction f dans les deux appels. Donc le fait le fait de séparer ces deux implémentations de f dans deux header différents avec cette modification de comportement est-il "standard" (je sais Visual un peu permissif et n'ai pas gcc sous la main). Cela veut-il bien dire que je peux ajouter un nouveau possible comportement au constructeur de MaClasseDeTest en incluant simplement un nouveau header contenant une nouvelle 'version' de f?

    J’espère avoir été clair (ce qui n'est pas certain) et merci pour vos réponses.

  2. #2
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Que ça soit en C ou C++, lors de la compîlation tu peux avoir un fichier par fonction par exemple (En C j'utilise souvent cette architecture de fichier pour m'y retrouver facilement).

    Ensuite c'est le compilo qui s'amusera à rassembler les pieces du puzzle.

    Tu peux donc créer un fichier par implémentation du moment que tes headers regroupent bien ces implémentations au bon endroit.

    C'est le même principe qu'un fichier cpp par implémentation de classe (pour les classes qui héritent par exemple).

    Il faut juste faire attention à une chose:

    Que ton protype de ton implémentation se retrouve au bon endroit lors de l'inclusion du .h
    Sinon ton compilo ne saura pas quelle pièce du puzzle correspond à cet appel de fonction

  3. #3
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Petite remarque*: ton exemple initial est faux (du moins, le commentaire).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
       MaClasseDeTest t1(10); // call #1 
     
       Test temp;
        MaClasseDeTest t2(temp);  // call #2 without error thanks to SFINAE
    La première ligne appelle #2, la deuxième appelle #1, contrairement à ce qu’indique ton commentaire (mais l’exemple wiki que tu as repris est juste, c’est bien ta modification qui a induit ce comportement).

    Sinon, par rapport à ta question : il faut bien comprendre que l’inclusion des fichiers .h est une étape indépendante, et qui a lieu avant la compilation à proprement parler. C’est à dire que, dans l’ordre, ton compilateur va :

    - lire les fichiers, et procéder à l’inclusion des fichiers .h pour générer un fichier cpp
    - compiler le fichier cpp généré

    Dès lors, éparpiller le code dans différents fichiers .h ne change strictement rien si tu les inclus tous à la fin :*au final, cela se retrouve dans le même fichier. C’est en revanche différent pour les fichiers .cpp, qui eux sont compilés séparément, et où c’est l’édition de liens qui se charge de les regrouper (avec donc, des contraintes différentes).

  4. #4
    Membre averti Avatar de vikki
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    292
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mai 2007
    Messages : 292
    Points : 302
    Points
    302
    Par défaut
    Salut et merci pour ta réponse.

    Donc à priori ce comportement est "normal", à l'appel du constructeur template, le compilateur va bien chercher l’implémentation adéquate de f en fonction des définitions disponibles à cette instant, même si ces définitions et l'implémentation de MaClasseDeTest sont tous dans des header différents. Ce comportement est le même dans le cas de surcharge de fonction et de spécialisation de fonction template.

  5. #5
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Oui (quoi dire de plus? )

  6. #6
    Membre averti Avatar de vikki
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    292
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mai 2007
    Messages : 292
    Points : 302
    Points
    302
    Par défaut
    Je n'avais pas vu le message de white tentacle. Effectivement mes commentaires sont faux, c'est d'abord l'appel de #2 puis #1. Tu confirmes bien la réponse de skeud, à savoir que le morcellement en différents header ne change rien.

    J'explique un peu l'objectif final puisque je ne suis pas bien sûr de la validité de l'approche. L'idée est d'implémenter une classe conteneur générique proche de boost::any. Je trouve celui ci un peu limitant dans le sens où (à ma connaissance) il n'est pas possible de caster un boost::any<Type> en OtherType, typiquement quelque chose comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    boost::any a(2.5);
    int val = boost::any_cast<int>(a);
    L'idée est de définir une classe de type any en laissant la possibilité d'ajouter des comportement différent en fonction du type de donnée sous-jacent. L’implémentation serait du type:
    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
     
    //MyAny.h
    #pragma once
    #include <iostream>
     
    class BaseHolder
    {
    protected:
    	virtual ~BaseHolder(){}
    };
     
    template< class T>
    class Holder : public  BaseHolder
    {
    public:
     
    	T value;
     
    	Holder(T val)
    		:value(val)
    	{}
     
    	virtual ~Holder()
    	{}
    };
     
    //les fonctions createHolder et castHolder peuvent etres redefinient pour specifier un comportement different
    template<class T>
    BaseHolder * createHolder(T value)
    {
    	return new Holder<T>(value);
    }
     
     
    template< class T>
    T castHolder(BaseHolder * holder, T)
    {
    	return static_cast<Holder<T>*>(holder)->value;
    }
     
    class MyAny
    {
    	BaseHolder * m_holder;
     
    public:
     
    	template< class T>
    	MyAny( T value)
    	{
    		m_holder = createHolder<T>(value);
    	}
     
    	template< class T>
    	T cast()
    	{
    		return castHolder<T>(m_holder,T());
    	}
    };
    avec par exemple un comportement différent dans le cas de valeurs arithmétiques:

    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
    //MyAnyArithmetic.h
    #pragma once
    #include <iostream>
    #include <boost/variant.hpp>
    #include <boost/utility/enable_if.hpp>
    #include <boost/type_traits/is_arithmetic.hpp>
    #include "MyAny.h"
     
    class NumberHolder : public  BaseHolder
    {
    public:
     
    	typedef boost::variant<bool,char,int,double> number_type;
     
    	number_type value;
     
    	template<class T>
    	NumberHolder(T val)
    		:value(val)
    	{}
     
    	virtual ~NumberHolder()
    	{}
    };
     
    template<class CastTo>
    struct visitor_cast
    	: public boost::static_visitor<CastTo>
    {
     
    	template<class T>
    	CastTo operator()(T & value)
    	{
    		return static_cast<CastTo>(value);
    	}
    };
     
     
    //nouvelles définitions des fonctions createHolder et castHolder appelées pour les parametres de type arithmetique
    template<class T>
    BaseHolder * createHolder(typename boost::enable_if<boost::is_arithmetic<T>, T>::type value)
    {
    	return new NumberHolder(value);
    }
     
    template< class T>
    typename boost::enable_if<boost::is_arithmetic<T>, T>::type castHolder(BaseHolder * holder, typename boost::enable_if<boost::is_arithmetic<T>, T>::type)
    {
    	visitor_cast<T> vis;
    	return boost::apply_visitor(vis,static_cast<NumberHolder*>(holder)->value);
    }
    En incluant ce header, il est possible d'écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    MyAny any(2.5);
    int value = any.cast<int>();
    Voila l'idée générale, n’hésitez pas à me dire cela vous semble foireux.

  7. #7
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    T castHolder(BaseHolder * holder, T)
    me semble foireux, je suis pas sur que tu pourras compiler.

    Faire une classe me semble un peu énorme pour le besoin, pourquoi tu ne fais pas une fichier.cpp avec une fonction surcharger pour chaque type dont tu as besoin:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    void convert(std::string toto,int &ret);
    void convert(int toto,std::string &ret);
    void convert(int toto,char *&ret);
    void convert(std::string toto,char *&ret)
    ......
    Enfin moi j'avais fait comme ça, peut-etre ce n'est pas la bonne approche ou c'est pas l'utilité que tu veux

  8. #8
    Membre averti Avatar de vikki
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    292
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mai 2007
    Messages : 292
    Points : 302
    Points
    302
    Par défaut
    T castHolder(BaseHolder * holder, T)
    me semble foireux, je suis pas sur que tu pourras compiler.
    Ça compile sous Visual . Le paramètre T est la pour supprimer une ambiguïté sur le choix de l’implémentation de castHolder (mais c'est clairement pas très élégant).

    Faire une classe me semble un peu énorme pour le besoin, pourquoi tu ne fais pas une fichier.cpp avec une fonction surcharger pour chaque type dont tu as besoin:
    Malheureusement, le but initial est avant tout de manipuler un conteneur générique, je ne peux pas couper à une classe de type any. Je cherche simplement à avoir quelque chose d'un poil différent de boost.any notamment par rapport aux possibilités de cast.

  9. #9
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Décidémment visual à vraiment un comportement étrange

    Bon dans ce cas ça me semble a peuprès correct après un bref coup d'oeil

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. appliquer plusieurs templates
    Par Manu_Just dans le forum XSL/XSLT/XPATH
    Réponses: 7
    Dernier message: 04/04/2003, 16h26
  3. template match="node() mais pas text()"
    Par Manu_Just dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 26/03/2003, 10h52
  4. [ActiveX] Visibilité d'une propriété
    Par paradise dans le forum Composants VCL
    Réponses: 2
    Dernier message: 14/11/2002, 18h33
  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