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 :

sizeof() et preprocesseur


Sujet :

C++

  1. #1
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut sizeof() et preprocesseur
    Je viens de faire un constat contrariant.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #define N sizeof(long)
    #if N == 8
    ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #define N 4
    #if N == 8
    ...
    Le premier code est refusé, le second accepté
    Pourquoi qu'il aime pas les sizeof() ?

  2. #2
    Membre Expert
    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
    Par défaut
    Parce que sizeof n'est pas géré par le préprocesseur, mais par le compilateur. Et le préprocesseur refuse de comparer des chaînes et des entiers (je ne suis même pas sûr qu'il sache comparer des chaînes, j'ai souvenir que non).

  3. #3
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Oui, ça se tient. Logique quelque part.
    Mais bon, ce ne serait pas du luxe si le preprocesseur pouvait évaluer les sizeof().

    Merci.

  4. #4
    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
    Par défaut
    Citation Envoyé par camboui Voir le message
    Mais bon, ce ne serait pas du luxe si le preprocesseur pouvait évaluer les sizeof().
    La taille des types est définie par le compilateur. Donc le preprocesseur n'en a aucune idée en fait.

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Il faut aussi comprendre dans quel ordre tout s'effectue, lorsque tu lance une compilation, entre le code source que tu as écrit et le binaire exécutable que tu obtiens...

    Pour faire simple (et donc, de manière pas tout à fait juste), on peut dire qu'il y a cinq étapes distinctes:
    1. Les commentaires sont supprimé du code
    2. Le préprocesseur passe le premier, et gère ses propres directives (#ifdef, #define, inlcude, ...)
    3. Le compilateur converti ce qui reste en code assembleur
    4. L'assembleur génère les instructions processeur (binaire objet)
    5. L'éditeur de liens relie les fichiers binaires objets de manière à en faire un fichier unique cohérent
    Bon, s'il faut être honnête, les compilateurs actuels ne sont plus obligé de passer par une étape en code assembleur, mais ils sont toujours capables de fournir un tel code , et l'éditeur de liens n'intervient que pour les liens internes d'un projet, à condition que ce ne soit pas pour faire une bibliothèque statique...

    Par contre, les premiers compilateurs suivaient exactement ce genre de séquence

    Et le fait est que l'opérateur sizeof est pris en compte... au moment de créer le code assembleur (lors de l'étape 3, en sommes)...

    C'est à dire, bien après que le préprocesseur soit passé par là

    Par contre, l'opérateur sizeof est une constante de compilation, ce qui fait que la valeur obtenue grace à lui peut parfaitement être interprétée par le compilateur au moment de la compilation, et non au moment de l'exécution...

    Lorsque l'on sait que l'opérateur ternaire ? (qui peut être utilisé pour créer un test sous la forme de <expression constante> ? <faire si Vrai> : <faire si faux> ) peut, lui aussi, être interprété au moment de la compilation, il est tout à fait possible d'envisager de l'utiliser (les utiliser) lorsque l'on travaille avec les templates:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T, typename U>
    struct unTestQuelconque
    {
        enum {value = (sizeof(T)<sizeof(U) ) ? -1 : 1} ;
    };
    fera que la valeur obtenue par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    unTestQuelconque<char, long long>::value
    sera évaluée, au moment de la compilation à -1 alors que la valeur obtenue par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    unTestQuelconque<long long, char>::value
    sera évaluée à 1 (au moment de la compilation, toujours)
    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
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Intéressant.
    Donc ce genre de code est "incorrect" ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #ifndef int16
    typedef __int16 int16;
    #endif
    Le typedef n'est défini qu'à la compilation, le preprocesseur ne verra jamais la définition de int16.
    D'ordre plus général, les types n'existent pas pour le preprocesseur donc je suppose qu'il est inutile de faire quelque chose comme ceci:C'est alors un peu compliqué de faire du code portable. Il faut connaitre à l'avance quels sont les types reconnus par le compilateur.

    Etant donné, que, quand même et malgré tout, preprocesseur et compilateur agissent de concert pour "lire" du code source, je ne verrais pas d'objection à ce qu'une extension soit faite aux preprocesseurs afin qu'ils connaissent les types primitifs du compilateur associé, de même que certaines "fonctions" qui y sont liées (comme sizeof() ou typeid()).
    On ne prévoirait pas ça pour la norme C0x++ ?

  7. #7
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    D'un autre côté, en C++, c'est moins un problème qu'en C, vu qu'on peut utiliser la métaprogrammation pour avoir des ifs statiques quand même:
    Code C++ : 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< bool b >
    void UneFonctionSizeof(void);
     
    template<>
    void UneFonctionSizeof<true>(void)
    {
    	std::cout << "la condition sur sizeof est vraie" << std::endl;
    }
     
    template<>
    void UneFonctionSizeof<false>(void)
    {
    	std::cout << "la condition sur sizeof est fausse" << std::endl;
    	//UnTypeNonDeclare toto; mince, ceci ne marche pas.
    	//toto.Test();
    }
     
    void TestMetaSizeof(void)
    {
    	UneFonctionSizeof(sizeof(short int) == 2);
    }
    Malheureusement, comme l'indique mon commentaire, ça ne permet pas d'utiliser quelque chose qui ne compile pas quand la condition n'est pas satisfaite...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  8. #8
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Ainsi donc, impossible de savoir si par exemple le type "long long" est supporté, ni par preproc ni par metaprog.
    Ou si "__int64" est identique à "long long" (c'est le cas en VS2005, il n'existe pas en MSVC6).
    Ou si "__int32" est identique à "int" (c'est le cas en VS2005, ce ne l'est pas en MSVC6).

  9. #9
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Citation Envoyé par camboui Voir le message
    Ou si "__int32" est identique à "int" (c'est le cas en VS2005, ce ne l'est pas en MSVC6).
    Que veux-tu dire par là exactement?
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Que se sont des types identiques.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    void foo(int i) {}
    void foo(__int32 i) {}
    VS2005 n'accepte pas le code qui précède (MSVC6 l'accepte), pas plus que le suivant.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    void foo(long long i) {}
    void foo(__int64 i) {}
    Ce sont les mêmes prototypes de fonctions.

    En bref, difficile de dire quels types existent et/ou sont identiques pour un compilo donné.

  11. #11
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    D'un autre côté, sizeof ne permet pas non plus de le savoir, ça.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  12. #12
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Au delà du sizeof(), quelles autres astuces peut-on utiliser en métaprogramation ?

    Ainsi avec sizeof() on peut connaître et comparer la taille d'un type.
    De même, peut-on savoir si un type est primitif ou "complexe" ? (i.e. "int" est primitif, "struct S {};" est complexe)
    Peut-on savoir si un type (struct ou class) possède des méthodes virtuel ?
    Peut-on savoir si un type est une référence ou un pointeur ? (avec leur variante const ou pas)
    Peut-on savoir si un type est signé ou non ?
    Peut-on savoir si un type est const ou non ?

    J'ai par exemple essayé ceci qui ne compile pas:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template<I>
    void foo(I i)
    {
     //coment savoir si I est signé ou non ?
     unsigned I ui=i;
    }
     
    main()
    {
     foo<int>(25);
    }

  13. #13
    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
    Par défaut
    Salut,
    En général, on utilise des spécialisations de structure template :
    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<class T> struct is_signed
    {
    static const bool value = false;
    }; // par defaut
    // puis spécialisation pour les types signés :
    template<> struct is_signed<char>
    {
    static const bool value = true;
    };
    template<> struct is_signed<signed char>
    {
    static const bool value = true;
    };
    template<> struct is_signed<const signed char>
    {
    static const bool value = true;
    };
    //etc.. pour short, int, long
    Et là encore, c'est une version pas tout à fait correcte car elle ne gère pas les références/pointeurs et mille et autres petits détails du langage ou des compilateurs.
    En fait, si tu en as vraiment besoin, plutôt que de tout refaire (mal comme ci-dessus), tu as tout intérêt à piocher dans Boost.Types Trait. Regardes comme c'est implémenté pour apprendre un peu de méta-prog.

  14. #14
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Pour les types primitifs, vu que leur nombre est fini et qu'ils sont connus à l'avance, on peut faire une classe de traits.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

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

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Par défaut
    De même, peut-on savoir si un type est primitif ou "complexe" ? (i.e. "int" est primitif, "struct S {};" est complexe) : oui
    Peut-on savoir si un type (struct ou class) possède des méthodes virtuel ? hum, j'ai un doute mais je crois pas. (ou alors c'est basé sur le compilo)
    Peut-on savoir si un type est une référence ou un pointeur ? (avec leur variante const ou pas) oui
    Peut-on savoir si un type est signé ou non ? oui
    Peut-on savoir si un type est const ou non ? oui

    Pour tous, voir boost::type_traits.
    Sa se fait à la main aussi et c'est intéressant à faire. (boost::type_traits va très loin celà dis, ils utilisent les possibilités des compilos etc).
    Par exemple pour savoir si c'est une ref :
    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
     
    template <typename T>
    struct isRef
    {
        enum { Yes = 0 };
        enum { No = !Yes };
     
        typedef T baseType;
    };
     
    template <>
    struct isRef<T&>
    {
        enum { Yes = 1 };
        enum { No = !Yes };
     
        typedef T baseType
    };
    C'est un peu hardcore pour savoir si le type est une fonction si je me souviens bien mais rien d'insurmontable.


    edit : grillé deux fois :').

  16. #16
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Par contre, il me semble qu'on ne peut pas savoir si un type est POD.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  17. #17
    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
    Par défaut
    Citation Envoyé par Goten Voir le message
    Peut-on savoir si un type (struct ou class) possède des méthodes virtuel ? hum, j'ai un doute mais je crois pas. (ou alors c'est basé sur le compilo)
    oui : boost::is_polymorphic
    Ca se base sur le fait qu'une vtable ça prend de la place. Toujours (mal) écrit, ça voisine :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template<class T> struct is_polymorphique
    {
       private:
       struct dummy_1 : public T
       {
          ~dummy_1();
       };
       struct dummy_2 : public T
       {
          virtual ~dummy_2();
       };
       public :
       static const bool value = sizeof(dummy_1)==sizeof(dummy_2);
    };
    P.S. : comme dit dans mon code précédent, je n'ai aucun talent divinatoire. Je me contente pour apprendre de regarder l'implémentation de Boost.

  18. #18
    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
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Par contre, il me semble qu'on ne peut pas savoir si un type est POD.
    boost::is_pod
    Mais là, je laisse au lecteur curieux le soin de regarder l'implémentation boost.

  19. #19
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Boost "triche".
    Citation Envoyé par [url=http://www.boost.org/doc/libs/1_40_0/libs/type_traits/doc/html/boost_typetraits/reference/is_pod.html]Doc de boost[/url]
    Without some (as yet unspecified) help from the compiler, is_pod will never report that a class or struct is a POD; this is always safe, if possibly sub-optimal. Currently (May 2005) only MWCW 9 and Visual C++ 8 have the necessary compiler-__intrinsics.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

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

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Par défaut
    Ah oui en effet is_polymorphic j'avais oublié. Tricky :p.

    Ah et à noté que le prochain standard prévoit lui aussi une classe de trait pour les types .
    Nota : C'est utile par exemple pour avoir un "sélecteur" sur la façon de passez un argument à une fonction. (si le type est un type primitif on passe par valeur, si c'est un pointeur on laisse, si c'est autre chose une const_ref etc, boost propose ça aussi hein :p.)

    ps : j'avais édité mon message mais vous avait poster entre temps du coup il est passé à la trappe (enfin rester sur la première page quoi).

    edit : ouai voilà, is_pod est l'exemple typique où ils ont besoin de faire appel aux fonctions du compilos (et donc c'est pas _portable_)

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 3 123 DernièreDernière

Discussions similaires

  1. sizeof() d'une structure
    Par tut dans le forum MFC
    Réponses: 12
    Dernier message: 29/08/2006, 18h21
  2. Sizeof d'un pointeur sur char ...
    Par Mike888 dans le forum C
    Réponses: 8
    Dernier message: 03/11/2005, 13h04
  3. [PREPROCESSEUR] quelle difference ??
    Par chronos dans le forum C++
    Réponses: 11
    Dernier message: 05/05/2005, 19h15
  4. sizeof
    Par drKzs dans le forum C
    Réponses: 6
    Dernier message: 04/10/2003, 23h48
  5. Erreurs (sizeof)
    Par deepfear dans le forum C
    Réponses: 13
    Dernier message: 25/09/2003, 13h56

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