2009-10-10 7 views
5

Czy istnieje przenośna metoda w C, aby znaleźć maskę dla pola bitowego w czasie kompilacji?Maski bitfield w C

Idealnie chciałabym móc atomowo jasne pole tak:

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

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

przeciwnym razie muszę wyjąć zamek w struktury, która jest więcej niż wagi ciężkiej chciałbym.

+1

Funkcja __sync_and_fetch nie działa na polach bitowych: "GCC zezwoli na użycie dowolnego scalaru skalarnego lub wskaźnika o długości 1, 2, 4 lub 8 bajtów." – sambowry

Odpowiedz

2

Lub, jeśli naprawdę chciał maskę:

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

Zauważ, że 'typeof()' jest rozszerzeniem GCC i nie jest przenośne dla innych kompilatorów. –

+0

Dziękuję Keith - fajny hack. – Grandpa

+0

Z ciekawości, dlaczego jest to akceptowane, jeśli nie jest przenośne? Po prostu ciekawy. – BobbyShaftoe

1

Nie sądzę, jest to możliwe - nawet z offsetof(), który działa na przesunięcia bajtów, ale nie wydaje się działać dla bitfields. Będę redeclare pola jako enums/definiuje (0x01 0x02 itp.) I zarządzać bity siebie, dzięki czemu można uzyskać swoje zmiany atomowe.

2

Można zrobić coś takiego:

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

Aby atomowo jasne pole myRef, zrobić

(+ kod niepokazana obsłużyć gdy compare_and_swap nie)

2

I don” t wie, jak to zrobić w czasie kompilacji, ale w czasie wykonywania powinno to być proste podłączenie instancji twojej struktury bitfield z odpowiednio wielkości unsigned int i ustawienie wszystkich pól na 0 ex cept, na którym ci zależy, który powinien być ustawiony na wszystkie 1s - wartość unsigned int jest wtedy masą bitową, którą chcesz. Możesz to zrobić dla każdego pola przy starcie, może z makrem, aby uniknąć powtarzalnego kodu. Czy to nie wystarczy?

+0

+1 Mogę sobie wyobrazić, że to działa. Mogą pojawić się owłosione problemy z endianizmem i bitfieldami, ale mogę sobie wyobrazić, że jest to bezpieczne. Ale nie jestem guru standardów. –

0

Tak, można to zrobić. Musisz przechwycić wartość i wykonać operację. Następnie musisz użyć porównania atomów i wymiany (jak InterlockedCompareExchange w systemie Windows), aby zapisać nową wartość, jeśli pamięć nadal zawiera starą wartość. Jeśli ktoś zmodyfikował wartość, zapętlasz i spróbuj ponownie. Zauważ, że jest to standardowy wzorzec do wykonywania dowolnej operacji na danych o rozmiarze słowa, w których nie jest dostępna właściwość wewnętrzna.

Poniższy kod używa int - jak Keith wskazał, że możesz użyć unii, aby móc uzyskać wartości struktury jako int.

int oldValue, newValue; 
do 
{ 
    oldValue = myRef; 
    newValue = oldValue & ~AGE_MASK; 
} while (InterlockedCompareExchange(&myRef, newValue, oldValue) != oldValue); 
Powiązane problemy