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 :

Faire un sizeof "exact"


Sujet :

Langage C++

  1. #1
    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 Faire un sizeof "exact"
    Hello,

    J'aimerais connaître la taille théorique d'une classe, à savoir l'addition de la taille de chacun de ses membres, avant alignement.

    Idéalement, cette valeur se retrouverait sous forme d'un entier statique de la classe, éventuellement initialisé par une addition des sizeof des membres. Mais j'aimerais que cette valeur soit calculée à la compilation.

    Comment faire ?

    PS : je ne dispose pas des fonctions de C++11 concernant l'alignement

  2. #2
    Membre émérite
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    852
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 852
    Points : 2 298
    Points
    2 298
    Par défaut
    Et bien déjà fais des sizeof de toutes tes variables. Ensuite je présume que chaque méthode est un pointeur sur fonction donc doit avoir la taille d'un void*. Ca devrait déjà donné une idée. Cependant il me semble que les compilateurs font du "bourrages" pour faire en sorte que la taille des structures soient multiples de 8. J'attends donc de voir les réponses de ceux qui posteront après moi pour enrichir ma culture perso dans ce domaine.

    Sinon pourquoi ne pas tout simplement 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
    //T.hpp
     
    class T
    {
    public:
     T();
     virtual ~T();
     
    private;
     int tata;
     char toto;
     static T *_instance;
    };
     
    //T.cpp
     
    T *T::_instance = sizeof(T);
    ? Ca ne te renvoie pas la valeur attendue ?

  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,

    De manière générale, tous les types primitifs (en ce, y compris les pointeurs qui ne sont que des valeur numérique entière ) ont, d'office, une taille correspondant à un multiple de la taille d'un char et ca, c'est garanti par la norme.

    Ce qui se passe lorsque tu crées une structure manipulant des types différents, c'est que le processeur ne peut accéder qu'à des adresses séparées entre elles par la taille d'un entier (généralement 4 bytes) et les données doivent donc être alignées de manière à ce que chaque donnée soit accessible à une adresse particulière.

    Cela se fait en priorité au niveau de chaque donnée de la structure, puis de manière "globale" (si nécessaire) de manière à ce que deux instances consécutives de ta structure puissent être accessibles à des adresses valides.

    Les fonctions membres n'augmentent pas la taille de la structure!!! (en fait, une fois que le compilateur est passé par là, les fonctions membres sont traitées comme des fonction classique dont le premier paramètre est d'office un pointeur sur l'objet courent (this) ) Mais il est possible que la structure dispose d'un (voire plusieurs) pointeur(s) permettant de représenter la vtable (la table des fonctions virtuelle).

    La taille réellement nécessaire à la représentation d'une structure en mémoire (hors tout alignement) serait donc "simplement" à la somme des taille de ses membres de type primitifs + la taille des pointeurs éventuels sur la vtable.

    Par chance, l'opérateur sizeof (hé oui, c'est considéré comme un opérateur ) fournit des constantes de compilation.

    Tu pourrais donc "assez facilement" envisager une structure template simple proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    /* l'implémentation par défaut fonctionne pour tous les types primitifs */
    template <typename T>
    RealSizeHolder{
        enum{size = sizeof(T)};
    };
    /* il faudra sans doute prévoir une spécialisation pour les pointeurs sur types primitifs ;) */
    qu'il te faudra, malheureusement, spécialiser pour tout type pour lequel tu voudrais obtenir la taille hors alignement, sous une forme proche 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
    20
    /* soit la structure S1 proche de */
    struct S1{
        char c;
        int i;
    };
    /* occasionnerait la spécialisation */
    template <>
    RealSizeHolder<S1>{
        enum{size = RealSizeHolder<char>::size + realSizHolder<int>::size};
    };
    /* qui serait (S1) utilisée dans la structure S2 sous une forme proche de */
    struct S2{
        S1 s1[3];
        int i;
    };
    /* qui occasionnerait la spécialisation */
    template <>
    RealSizeHolder<S2>{
        enum{size = (RealSizeHolder<S1>::size*3) + realSizHolder<int>::size};
    };
    Après, il y aura le problème des classes qui utilisent des fonctions virtuelles, mais un petit tour du coté du fichier type_traits devrait pouvoir te donner des pistes intéressantes pour déterminer s'il y a ou non une table de fonctions virtuelles (je pense, entre autres, à is_polymorphic et à has_virtual_destructor )
    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 é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
    En fait, je voulais que l'info soit dans le type même, et pas passer par une autre classe comme RealSizeHolder. Je n'ai pas besoin de l'appliquer aux membres, car ces derniers sont de toute façon de type primitif.

    J'avais complètement oublié l'astuce de l'enum. C'est cela qui me manquait.

    Je peux ainsi faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    struct MaClasse
    {
        enum { size = sizeof(char) + sizeof(int) + sizeof(float); }
     
        char monChar;
        int moInt;
        float monFloat;
    };
    Idéalement, il eût fallu que size se débrouille pour trouver sa valeur en fonction des membres qu'on enlève ou ajoute, mais je sais que n'est pas possible...

    Merci koala01 !

  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
    En fait, je ne saurais que trop te conseiller de quand meme passer par une (spécialisation de) classe template dans ton code, selon le principe de la responsabilité unique et le fait que tout problème en informatique peut etre résolu en ajoutant un niveau d'indirection supplémentaire.

    Quitte à rajouter simplement l'énumération dans ta structure réelle 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
    struct MaClasse { // pour reprendre ton exemple :D
    enum {size = RealSizeHolder<MaClasse>::size}; // pour reprendre le mien
     
        char monChar;
        int moInt;
        float monFloat;
    };
    /* OU OU OU (peut etre meme préférable :D */
    struct MaClasse : public RealSizeHolder<MaClasse>{ 
     
        char monChar;
        int moInt;
        float monFloat;
    };
    cela te donnera une sécurité supplémentaire lorsque tu en viendra à vouloir créer des structures qui utilisent tes structures "de base".

    En effet, la valeur énumérée sera, de toutes façons, une constante de compilation avec tout ce que cela comporte, mais, si tu en viens à créer une structure proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct S2 : public RealSizeHolder<S2>{
    MaClasse c;
    int i;
    }
    et que tu t'attends (comme en atteste l'héritage ) à pouvoir disposer de la taille réellement requise, tu auras une erreur de compilation au motif que "RealSizeHolder<T> ne dispose pas du membre size (avec T = S2 )" si tu ne spécialise pas RealSizeHolder pour S2, enfin, sous résèrve d'une petite modification de mon exemple précédant 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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    /* aucune définition complete par défaut */
    template <typename T>
    struct RealSizeHolder;
    /* par contre, pour un pointeur, ca fonctionne par défaut */
    template <typename T>
    struct RealSizeHolder<T*>{
        enum{size = sizeof(T*)};
    };
    template <>
    struct RealSizeHolder<char>{
    enum {size =sizeof(char)};
    };
    template <>
    struct RealSizeHolder<short>{
    enum {size =sizeof(short)};
    };
    template <>
    struct RealSizeHolder<int>{
    enum {size =sizeof(int)};
    };
    template <>
    struct RealSizeHolder<long>{
    enum {size =sizeof(long)};
    };
    template <>
    struct RealSizeHolder<long long>{
    enum {size =sizeof(long long)};
    };
    /* + les memes en unsigned si besoin */
    template <>
    struct RealSizeHolder<float>{
    enum {size =sizeof(float)};
    };
    template <>
    struct RealSizeHolder<double>{
    enum {size =sizeof(double)};
    };
    template <>
    struct RealSizeHolder<long double>{
    enum {size =sizeof(long double)};
    };
    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 é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
    Je trouve que ça n'a pas beaucoup de sens de déporter cette information dans une autre classe (RealSizeHolder), car la valeur de cet enum est directement dépendante du contenu de MaClasse. Il est je trouve plus clair de tout localiser ua même endroit.

  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 oodini Voir le message
    Je trouve que ça n'a pas beaucoup de sens de déporter cette information dans une autre classe (RealSizeHolder), car la valeur de cet enum est directement dépendante du contenu de MaClasse. Il est je trouve plus clair de tout localiser ua même endroit.
    En fait, tu ne déportes pas la valeur en elle même, mais tu déportes le calcul de cette valeur, ce qui est fondamentalement différent.

    Comme tu peux le constater dans le code que j'ai fourni, ta classe a, de toutes manières, une valeur énumérée sous le nom de size, que ce soit parce qu'on la déclare explicitement ou grâce au CRTP.

    Cependant, il y a plusieurs avantages à le faire de la sorte:

    1. Sur des structure plus ou moins complexes, tu seras en mesure de déporter le code du calcul (meme si ce n'est que des additions), et donc de garder ta structure plus lisible
    2. le fait de déporter le calcul fait de la classe modèle une "politique" que tu peux adapter indépendamment de tout le reste
    3. Si tu ne définis pas la spécialisation pour une structure donnée, tu le sauras directement à la compilation, surtout si tu as affaire à des structures "complexes".
    Un simple exemple pour le troisième argument.

    Imaginons que tu aies les structures S1, S2 et S3 qui sont des structures "relativement simples" comprends: composées au maximum de quelques données de types primitifs, et que tu aies une structure S4 qui se présenterait sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct S4{
    S1 firstData;
    S2 secondData;
    S3 thirdData;
    };
    pour laquelle tu veuilles disposer de l'information de taille minimale.

    En spécialisant ta politique sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template<>
    struct RealSizeHolder<S4>{
       enum {size = RealSizeHolder<S1>::size +RealSizeHolder<S2>::size +RealSizeHolder<S3>::size};
    };
    tu auras la certitude que la spécisalisation de RealSizeHolder pour S1, S2 et S3 existe bel et bien, et que tu peux, en cas de besoin, aller la changer "sans difficulté"(que S1, S2 et S3 aient ou non la valeur énumérée size ne changera rien )

    Finalement, c'est une stricte application du principe de ségrégation des interfaces : la structure RealSizeHolder fournit un comportement bien déterminé qui est utilisé par "tout ce qui peut en avoir besoin" sous différentes formes, alors que si tu effectue "simplement" le calcul au niveau de l'énumération elle-même, tu en arrives, en gros, à donner deux responsabilités distinctes à tes structures: celle de calculer leur taille à la compilation et... celle de maintenir cette taille à l'exécution.
    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

+ Répondre à la discussion
Cette discussion est résolue.

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