Je travaille sur un code C ++ pour un système intégré. L'interface d'E / S Le code utilise nécessite que la taille de chaque message (en octets) soit une puissance de deux. En ce moment, le code fait quelque chose comme ceci (à plusieurs endroits):
#pragma pack(1) struct Message { struct internal_ { unsigned long member1; unsigned long member2; unsigned long member3; /* more members */ } internal; char pad[64-sizeof(internal_)]; }; #pragma pack()
11 Réponses :
Pourquoi ne pas utiliser un syndicat?
union Message { struct { unsigned long member1; /* more members */ }; char[64]; };
En réalité, cela est dangereux, car Member1 et Member2 ne seront pas alignés à la même adresse sur différentes plateformes. Comme il s'agit du code IO, cela entraînerait des erreurs.
Probablement la manière la plus évidente serait de simplement utiliser l'opérateur ternaire:
Un moyen de travailler autour du problème serait de remplacer le codé 64 avec un multiple de taille (longue), en tournant le rembourrage en quelque chose comme ceci:
char pad[4*sizeof(unsigned long) - sizeof(internal_)];
Que diriez-vous d'écrire un petit wrapper autour de la fonction de message d'envoi et de réception qui gère n'importe quel message de taille et qu'elles allouent simplement un tampon plus grand (puissance suivante de 2) et memclear, copiez-la au début et envoyez-la. p>
union Message { struct { unsigned long member1; unsigned long member2; //... }; char pad[1 << 5]; //change 5 to whatever size you need... }; Would be a little cleaner.
Utilisez un métaprogramme de modèle. (Édité en réponse au commentaire).
#include <iostream> #include <ostream> using namespace std; template <int N> struct P { enum { val = P<N/2>::val * 2 }; }; template <> struct P<0> { enum { val = 1 }; }; template <class T> struct PadSize { enum { val = P<sizeof (T) - 1>::val - sizeof (T) }; }; template <class T, int N> struct PossiblyPadded { T payload; char pad[N]; }; template <class T> struct PossiblyPadded<T, 0> { T payload; }; template <class T> struct Holder : public PossiblyPadded<T, PadSize<T>::val> { }; int main() { typedef char Arr[6]; Holder<Arr> holder; cout << sizeof holder.payload << endl; // Next line fails to compile if sizeof (Arr) is a power of 2 // but holder.payload always exists cout << sizeof holder.pad << endl; }
J'aime ça, mais il échoue lorsque le contenu est un pouvoir pair de 2. La déclaration de pad est une erreur C2466: impossible d'attribuer un tableau de taille constante 0
Je suppose que vous pouvez dériver la possibilité de typadrer de t au lieu d'avoir un membre T (pour t) et épargnez-vous la syntaxe d'accès aux membres ennuyeux.
@Fizzer, est-ce garanti pour ne pas ajouter de rembourrage? J'utiliserais un syndicat moi-même. Et vous pouvez toujours faire un t * opérateur -> () ...
Merci! J'aime aussi la réponse de Bdonlan, mais je pense que c'est probablement une meilleure solution générale.
Aimer! J'ai trouvé cela en recherchant un modèle de métaprogrammation de modèle d'obtenir la prochaine puissance de deux à l'heure de la compilation. Fonctionne très bien!
J'aime La réponse de Niki , surtout la partie avec des structures anonymes. p>
Une chose que la réponse n'a pas été résolvée était le problème de plus de 64 octets, mais cela peut être résolu en déclarant de manière conditionnelle un membre
Vous pouvez obtenir une constante de temps de compilation pour la taille de la structure arrondi jusqu'à une puissance de deux modèles:
template<int N, int C = 1> struct Recurse { enum {result = Recurse<N/2, C*2>::result}; }; template<int C> struct Recurse<0, C> { enum {result = C}; }; template<typename T> struct Calc { enum {size = Recurse<sizeof(Test)-1>::result}; }; struct Test { int a; double b; double c; }; int main() { std::cout << sizeof(Test) << " -> " << Calc<Test>::size << std::endl; return 0; }
Et pourtant une autre solution de modèle (volant extrêmement de Fizzer ):
#include <iostream> #include <ostream> using namespace std; template <int TSize, int PSize = 1, bool = false> struct PadSize { static const int val = ( PadSize < TSize, (PSize*2), (TSize <= (PSize*2) ) > :: val ); }; template < int TSize, int PSize> struct PadSize <TSize, PSize, true> // As soon as TSize is <= to PSize { static const int val = PSize; }; int main() { typedef char Arr[8]; char pad[ PadSize <sizeof(Arr)>::val ]; cout << sizeof pad << endl; }
Vous utilisez déjà #pragma pack code>, je ne sais pas quel compilateur (s) votre utilisation spécifiquement, mais vous devriez voir si elles prennent en charge les arguments de pack qui contrôlent l'alignement / le rembourrage et Ensuite, vous pouvez simplement vous débarrasser du champ de rembourrage. Je sais version de MSVC de
Pragma Pack Code> prend en charge cela, de même que GCC's . < / p>
Vous pouvez macroize Ce comme suit (pour une architecture 32 bits ):
#pragma pack(1) struct Message { struct internal_ { unsigned long member1; unsigned long member2; unsigned long member3; /* more members */ } internal; char pad[alignment_padding(sizeof(internal_))]; }; #pragma pack()
J'aime ça, je n'avais pas pensé utiliser une macro pour traiter le N | n >> 1,2,4,8 ... etc.