2009-10-10 2 views
5

C'è un modo portatile in C per trovare la maschera per un campo di bit in fase di compilazione?Maschere bitfield in C

Idealmente, mi piacerebbe essere in grado di atomicamente chiaro un campo come questo:

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

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

Altrimenti devo prendere un blocco sul struct, che è più pesante di quanto mi piacerebbe.

+1

__sync_and_and_fetch non funziona su bitfields: "GCC permette qualsiasi scalare o puntatore tipo integrale che è 1, 2, 4 o 8 byte di lunghezza." – sambowry

risposta

2

Oppure, se si voleva davvero la maschera:

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

Si noti che 'typeof()' è un'estensione GCC e non è portabile ad altri compilatori. –

+0

Grazie mille Keith - bel trucco. – Grandpa

+0

Per curiosità, perché è accettato se non è portatile? Solo curioso. – BobbyShaftoe

1

Non penso sia possibile, anche con offsetof() che funziona per offset byte ma non sembra funzionare per bitfield. Vorrei ridichiarare i campi come enum/define (0x01 0x02 etc) e gestire i bit da soli, in modo da poter ottenere le modifiche atomiche.

2

Si potrebbe fare qualcosa di simile:

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

Per atomicamente chiaro un campo di mio_rif, fare

(+ codice unshown da gestire quando compare_and_swap fallisce)

2

I don' so come farlo in fase di compilazione, ma in fase di esecuzione dovrebbe essere una semplice questione dell'unione di un'istanza della struttura bitfield con un int unsigned di dimensioni appropriate e l'impostazione di tutti i campi su 0 ex Cettone quello che ti interessa su quale dovrebbe essere impostato su tutti gli 1 - il valore dell'int unsigned è quindi la maschera di bit che desideri. Puoi farlo per ogni campo all'avvio, magari con una macro per evitare qualche codice ripetitivo. Non potrebbe essere sufficiente?

+0

+1 Posso immaginare che funzioni. Potrebbero esserci problemi di peluria con endianness e bitfield, ma posso immaginare che questo sia sicuro. Ma io non sono un guru degli standard. –

0

Sì, questo può essere fatto. È necessario acquisire il valore e fare l'operazione. Quindi è necessario utilizzare un confronto e scambio atomico (come InterlockedCompareExchange su Windows) per memorizzare il nuovo valore se la memoria contiene ancora il vecchio valore. Se qualcuno ha modificato il valore, fai un loop e riprova. Si noti che questo è uno schema standard per eseguire qualsiasi operazione su una porzione di dati di dimensioni word in cui non è disponibile un'intrinseca.

Il codice seguente utilizza un int - come ha sottolineato Keith, è possibile utilizzare un'unione per poter ottenere i valori della struct come un int.

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