2009-10-10 5 views
5

Existe-t-il une méthode portable dans C pour trouver le masque pour un champ de bits au moment de la compilation?Masques de champ de bits dans C

Idéalement, je voudrais pouvoir un champ atomiquement clair comme ceci:

struct Reference { 
    unsigned age : 3; 
    unsigned marked : 1; 
    unsigned references : 4; 
}; 

struct Reference myRef; 
__sync_and_and_fetch(&myRef, age, ~AGE_MASK); 

Sinon, je dois prendre un verrou sur la struct, qui est plus lourd que je voudrais.

+1

__sync_and_and_fetch ne fonctionne pas sur les champs de bits: « GCC permettra à tout type scalaire ou pointeur intégral qui est 1, 2, 4 ou 8 octets de longueur. » – sambowry

Répondre

2

Ou, si vous voulez vraiment le masque:

union Reference { 
    unsigned asWord; 
    struct { 
    unsigned age : 3; 
    unsigned marked : 1; 
    unsigned references : 4; 
    } asFields; 
} 

Reference agemask_ref; 
agemask_ref.asFields = (typeof(agemask_ref.asFields)){0, -1, -1}; 
unsigned agemask = agemask_ref.asWord; 
+2

Notez que 'typeof()' est une extension GCC et n'est pas portable vers d'autres compilateurs. –

+0

Merci Keith - joli hack. – Grandpa

+0

Par curiosité, pourquoi est-ce accepté s'il n'est pas portable? Juste curieux. – BobbyShaftoe

1

Je ne pense pas que ce soit possible - même avec offsetof() qui fonctionne pour les décalages d'octets mais ne semble pas fonctionner pour les champs de bits. Je voudrais redéclarer les champs comme enums/définit (0x01 0x02 etc) et gère les bits vous-même, de sorte que vous pouvez obtenir vos changements atomiques.

2

Vous pouvez faire quelque chose comme:

union Reference { 
    unsigned asWord; 
    struct { 
    unsigned age : 3; 
    unsigned marked : 1; 
    unsigned references : 4; 
    } asFields; 
} 

Pour atomiquement clair un champ de myRef, faire

(+ un code non représenté à gérer quand compare_and_swap échoue)

2

Je ne Je sais comment le faire au moment de la compilation, mais au moment de l'exécution, il devrait être simple d'unioner une instance de votre structure bitfield avec un entier non signé de taille appropriée, et de mettre tous les champs à 0 ex cept celui qui vous intéresse et qui doit être défini sur tous les 1 - la valeur de l'entier non signé est alors le masque de bits que vous voulez. Vous pouvez le faire pour chaque champ au démarrage, peut-être avec une macro pour éviter le code répétitif. Cela ne pourrait-il pas suffire?

+0

+1 Je peux imaginer que cela fonctionne. Il peut y avoir des problèmes avec l'endianness et les champs de bits, mais je peux imaginer que cela soit sûr. Mais je ne suis pas un gourou des normes. –

0

Oui, cela peut être fait. Vous devez capturer la valeur et faire l'opération. Ensuite, vous devez utiliser une comparaison et un échange atomiques (comme InterlockedCompareExchange sous Windows) pour stocker la nouvelle valeur si la mémoire contient toujours l'ancienne valeur. Si quelqu'un a modifié la valeur, vous bouclez et réessayez. Notez qu'il s'agit d'un modèle standard pour effectuer n'importe quelle opération sur un bloc de données de la taille d'un mot, lorsqu'une valeur intrinsèque n'est pas disponible.

Le code ci-dessous utilise un int - comme l'a souligné Keith, vous pouvez utiliser une union pour obtenir les valeurs de la structure comme un int.

int oldValue, newValue; 
do 
{ 
    oldValue = myRef; 
    newValue = oldValue & ~AGE_MASK; 
} while (InterlockedCompareExchange(&myRef, newValue, oldValue) != oldValue); 
Questions connexes