2009-10-01 5 views
1

J'ai une structure contenant un champ de bits, dont la taille peut varier. Exemple:Champs de bits de taille variable avec aliasing

struct BitfieldSmallBase { 
    uint8_t a:2; 
    uint8_t b:3; 
    .... 
} 

struct BitfieldLargeBase { 
    uint8_t a:4; 
    uint8_t b:5; 
    .... 
} 

et un syndicat d'accéder à tous les bits à la fois:

template<typename T> 
union Bitfield 
{ 
    T bits; 
    uint8_t all; // <------------- Here is the problem 

    bool operator & (Bitfield<T> x) const { 
     return !!(all & x.all); 
    } 
    Bitfield<T> operator + (Bitfield<T> x) const { 
     Bitfield<T> temp; 
     temp.all = all + x.all; //works, because I can assume no overflow will happen 
     return temp; 
    } 
    .... 
} 

typedef Bitfield<BitfieldSmallBase> BitfieldSmall; 
typedef Bitfield<BitfieldLargeBase> BitfieldLarge; 

Le problème est le suivant: Pour certaines classes de base BITFIELD, un uint8_t ne suffit pas. BitfieldSmall s'intègre dans un uint8_t, mais pas BitfieldLarge. Les données doivent être emballées aussi étroitement que possible (elles seront traitées par des instructions SSE plus tard), donc toujours utiliser uint16_t est hors de question. Existe-t-il un moyen de déclarer le champ "all" avec un type intégral, dont la taille est la même que celle du bitfield? Ou une autre façon d'accéder à des bits dans leur ensemble?

Je peux bien sûr renoncer à l'utilisation du modèle et déclarer explicitement toutes les sortes de champs de bits, mais je voudrais éviter la répétition de code (il y a une liste assez complète d'opérateurs et de fonctions membres).

Répondre

5

Vous pouvez également définir le type intégral comme paramètre de modèle.

template<typename T, typename U> 
union Bitfield 
{ 
    T bits; 
    U all; 
} 

typedef Bitfield<BitfieldSmallBase, uint8_t> BitfieldSmall; 
typedef Bitfield<BitfieldLargeBase, uint16_t> BitfieldLarge; 
+0

Parfois, la solution est si simple :-) – hirschhornsalz

1

J'ai appris à la dure que si la largeur de bit sur vars que vous utilisez est un moyen pratique d'obtenir le compilateur de faire votre masquage et le déplacement pour vous, vous ne pouvez pas faire des hypothèses sur l'ordre et le remplissage des membres dans la structure. Son compilateur dépendant et le compilateur change vraiment l'ordre et dépend de l'autre code dans votre projet.

Si vous voulez traiter un octet comme des champs discrets, vous devez vraiment le faire à la dure.

+0

La portabilité est secondaire, parce que je dois utiliser SSE de toute façon, c'est donc limité à x86_64 (et éventuellement x86). Pour les compilateurs que j'utilise (gcc, icc) je peux faire des hypothèses sûres sur la disposition des structures, c'est défini dans l'ABI. – hirschhornsalz

+0

mais il n'est pas garanti d'être le même dans les deux compilateurs. Ou dans la * prochaine * version de ces compilateurs. – jalf

+0

Les deux icc et gcc sont conformes à l'ABI x86_64. Il y a eu des bris d'ABI de temps en temps, mais ils essaient généralement très fort de minimiser l'impact. Si vous trouvez un cas où la structure de icc diffère de celle de gcc, vous pouvez même créer un bug, car icc essaie d'être aussi compatible avec gcc que possible. – hirschhornsalz

1

vous pouvez utiliser metaprogramming de modèle pour définir une fonction de modèle que les cartes de BitfieldSmallBase, BitfieldLargeBase, etc dans un autre type - uint8_t par défaut et à uint16_t pour BitfieldLargeBase comme une spécialisation de modèle et utiliser alors que comme ceci:

union Bitfield 
{ 
    T bits; 
    typename F<T>::holder_type all; 
}; 
+0

Une autre bonne solution. Mais je préfère la solution John Kugelmans, c'est encore plus simple. – hirschhornsalz

+0

John a une meilleure solution s'il y a peu de classes BitfieldSmall *; le mien est meilleur s'il y en a beaucoup, mais un ou deux se démarquent et exigent, disons uint16_t vs default uint8_t. – catwalk

+0

Il existe 3 classes de champs de bits, dont une de deux octets (jusqu'à maintenant). Parce que la déclaration de F :: holder_type et sa spécialisation est de plus de 3 lignes, la solution de Johns est meilleure dans ce cas, mais avec une très légère marge ;-) – hirschhornsalz

1

Vous voudrez peut-être envisager std::bitset ou boost::dynamic_bitset plutôt que de rouler les vôtres. Dans tous les cas, évitez les std::vector<bool>!

+0

Merci pour l'avertissement ;-) Les tableaux que j'utilise dans cette partie du programme sont de taille très fixe (contrairement aux structures de données sous-jacentes), donc j'évite std :: vector de toute façon. Et boost ne simplifierait pas les choses, parce que j'ai besoin d'accéder aux données via SSE (__m128i et autres) pour faire le travail réel. – hirschhornsalz

0

Faire le nombre d'octets dont vous avez besoin partie des paramètres du modèle:

template <typename T, int S=1> 
struct BitField 
{ 
    union 
    { 
     T bits; 
     unsigned char bytes[S]; 
    }; 
}; 

typedef Bitfield<BitfieldSmallBase, 1> BitfieldSmall; 
typedef Bitfield<BitfieldLargeBase, 2> BitfieldLarge; 
0

Comment cela?

#include <limits.h> 

template <class T> 
union BitField 
{ 
    T bits; 
    unsigned all : sizeof(T) * CHAR_BIT; 
}; 
Questions connexes