2010-09-30 6 views
6

Ho un set di bit di bit che vengono utilizzati in un programma che sto eseguendo il porting da C a C++.Perché il C++ supporta l'assegnazione esadecimale, ma manca l'assegnazione binaria? Quanto è meglio conservare le bandiere?

Per cominciare ...

Le bandiere nel mio programma precedentemente definiti come:

/* Define feature flags for this DCD file */ 
#define DCD_IS_CHARMM  0x01 
#define DCD_HAS_4DIMS  0x02 
#define DCD_HAS_EXTRA_BLOCK 0x04 

... Ora ho capito che # definisce per le costanti (contro le costanti di classe, ecc) sono generalmente considerati cattivi.

Questo solleva domande sul modo migliore per archiviare i bit flag in C++ e perché C++ non supporta l'assegnazione di testo binario a un int, come se fosse possibile assegnare numeri esadecimali in questo modo (tramite "0x"). Queste domande sono riassunte alla fine di questo post.

ho potuto vedere una soluzione semplice è quello di creare semplicemente costanti individuali:

namespace DCD { 
    const unsigned int IS_CHARMM = 1; 
    const unsigned int HAS_4DIMS = 2; 
    const unsigned int HAS_EXTRA_BLOCK = 4; 
}; 

Chiamiamo questa idea 1.

Un'altra idea che avevo era di utilizzare un enum intero:

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM = 1, 
     HAS_4DIMS = 2, 
     HAS_EXTRA_BLOCK = 8 
    }; 
}; 

Ma una cosa che mi preoccupa di questo è che è meno intuitivo quando si tratta di valori più alti, sembra ... cioè

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM = 1, 
     HAS_4DIMS = 2, 
     HAS_EXTRA_BLOCK = 8, 
     NEW_FLAG = 16, 
     NEW_FLAG_2 = 32, 
     NEW_FLAG_3 = 64, 
     NEW_FLAG_4 = 128 
    }; 
}; 

Chiamiamo questa opzione approccio 2.

Sto pensando di utilizzare la soluzione macro di Tom Torf:

#define B8(x) ((int) B8_(0x##x)) 

#define B8_(x) \ 
(((x) & 0xF0000000) >(28 - 7) \ 
| ((x) & 0x0F000000) >(24 - 6) \ 
| ((x) & 0x00F00000) >(20 - 5) \ 
| ((x) & 0x000F0000) >(16 - 4) \ 
| ((x) & 0x0000F000) >(12 - 3) \ 
| ((x) & 0x00000F00) >(8 - 2) \ 
| ((x) & 0x000000F0) >(4 - 1) \ 
| ((x) & 0x0000000F) >(0 - 0)) 

convertito inline funzioni, per esempio

#include <iostream> 
#include <string> 
.... 

/* TAKEN FROM THE C++ LITE FAQ [39.2]... */ 
class BadConversion : public std::runtime_error { 
public: 
    BadConversion(std::string const& s) 
    : std::runtime_error(s) 
    { } 
}; 

inline double convertToUI(std::string const& s) 
{ 
    std::istringstream i(s); 
    unsigned int x; 
    if (!(i >> x)) 
    throw BadConversion("convertToUI(\"" + s + "\")"); 
    return x; 
} 
/** END CODE **/ 

inline unsigned int B8(std::string x) { 
    unsigned int my_val = convertToUI(x.insert(0,"0x").c_str()); 
    return ((my_val) & 0xF0000000) >(28 - 7) | 
      ((my_val) & 0x0F000000) >(24 - 6) | 
      ((my_val) & 0x00F00000) >(20 - 5) | 
      ((my_val) & 0x000F0000) >(16 - 4) | 
      ((my_val) & 0x0000F000) >(12 - 3) | 
      ((my_val) & 0x00000F00) >(8 - 2) | 
      ((my_val) & 0x000000F0) >(4 - 1) | 
      ((my_val) & 0x0000000F) >(0 - 0); 
} 

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM  = B8("00000001"), 
     HAS_4DIMS  = B8("00000010"), 
     HAS_EXTRA_BLOCK = B8("00000100"), 
     NEW_FLAG  = B8("00001000"), 
     NEW_FLAG_2  = B8("00010000"), 
     NEW_FLAG_3  = B8("00100000"), 
     NEW_FLAG_4  = B8("01000000") 
    }; 
}; 

È pazzesco? O sembra più intuitivo? Chiamiamo questa scelta 3.

Quindi, per ricapitolare, le mie domande onnicomprensiva sono:

1. perché non C++ supportano un "0b" flag di valore, simile a "0x"?
2. Quale è lo stile migliore per definire le bandiere ...
i. Costanti avvolte nello spazio dei nomi.
ii. Lo spazio dei nomi ha avvolto enum di unsigned non assegnati direttamente.
iii. Spazio dei nomi avvolto in enum di interi non firmati assegnati usando una stringa binaria leggibile.

Grazie in anticipo! E per favore non chiudere questo thread come soggettivo, perché voglio davvero ottenere aiuto su quale sia lo stile migliore e perché C++ non sia costruito in capacità di assegnazione binaria.


EDIT 1

Un po 'di informazioni aggiuntive. Leggerò un bitfield a 32 bit da un file e poi lo testerò con questi flag. Quindi tienilo a mente quando pubblichi suggerimenti.

risposta

13

Prima di C++ 14, i letterali binari erano stati discussi senza sosta nel corso degli anni, ma per quanto ne so, nessuno aveva mai scritto una proposta seria per farlo entrare nello standard, quindi non è mai stato davvero passato il palcoscenico di parlarne.

Per C++ 14, qualcuno ha finalmente scritto una proposta e il comitato l'ha accettata, quindi se è possibile utilizzare un compilatore corrente, la premessa di base della domanda è falsa: è possibile utilizzare valori letterali binari, che hanno la forma 0b01010101.

In C++ 11, invece di aggiungere direttamente valori letterali binari, hanno aggiunto un meccanismo molto più generale per consentire valori letterali generici definiti dall'utente, che è possibile utilizzare per supportare binari, o base 64 o altri tipi di cose interamente . L'idea di base è quella di specificare un letterale numerico (o stringa) seguito da un suffisso, e puoi definire una funzione che riceverà quel letterale e convertirlo in qualsiasi forma tu preferisca (e puoi mantenere il suo stato come "costante" "anche ...)

Per quanto riguarda quale utilizzare: se possibile, i valori binari letterali incorporati in C++ 14 o superiore sono la scelta più ovvia. Se non è possibile utilizzarli, mi piacerebbe genere preferisco una variazione dell'opzione 2: un enum con inizializzatori in esadecimale:

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM = 0x1, 
     HAS_4DIMS = 0x2, 
     HAS_EXTRA_BLOCK = 0x8, 
     NEW_FLAG = 0x10, 
     NEW_FLAG_2 = 0x20, 
     NEW_FLAG_3 = 0x40, 
     NEW_FLAG_4 = 0x80 
    }; 
}; 

Un'altra possibilità è qualcosa di simile:

#define bit(n) (1<<(n)) 

enum e_feature_flags = { 
    IS_CHARM = bit(0), 
    HAS_4DIMS = bit(1), 
    HAS_EXTRA_BLOCK = bit(3), 
    NEW_FLAG = bit(4), 
    NEW_FLAG_2 = bit(5), 
    NEW_FLAG_3 = bit(6), 
    NEW_FLAG_4 = bit(7) 
}; 
+0

Significa "inline unsigned int bit (n) {1 << (n);}' giusto? ;) –

+1

@ Jason: No; gli inizializzatori dell'enumeratore devono essere espressioni costanti; non è possibile chiamare una funzione in un'espressione costante, quindi deve essere una macro. –

+0

hrmm ... Immagino che sia un buon momento per usare una macro? –

0

Immagino che la linea di fondo sia che non è veramente necessario.

Se si desidera utilizzare il binario per i flag, l'approccio di seguito è come faccio in genere. Dopo l'originale definire non dovrai mai preoccuparti di guardare "Messier" più grandi multipli di 2, come lei ha ricordato

int FLAG_1 = 1 
int FLAG_2 = 2 
int FLAG_3 = 4 
... 
int FLAG_N = 256 

si può facilmente verificare con

if(someVariable & FLAG_3 == FLAG_3) { 
    // the flag is set 
} 

E btw, seconda del compilatore (sto usando GNU GCC Compiler) può supportare "0b"

nota Modificato per rispondere alla domanda.

+0

Tuttavia, questa è un'estensione non portabile specifica del compilatore. Non fa parte del C++. –

+0

Utilizzando g ++ ottengo l'errore 'suffisso non valido 'b0101" sulla costante intera' ... quindi ovviamente l'uso di "0b" nell'assegnazione NON è sicuro per il cross-compilatore e non è supportato ufficialmente/una soluzione accettabile. –

+0

Interessante, prima che pensassi che fosse nello standard e che i compilatori avevano scelto di ignorarlo. Non era consapevole che era il contrario. –

7

Con l'opzione due, è possibile utilizzare lo spostamento a sinistra, che è forse un po 'meno "poco intuitivo:"

namespace DCD { 
    enum e_Feature_Flags { 
     IS_CHARMM =  1, 
     HAS_4DIMS =  (1 << 1), 
     HAS_EXTRA_BLOCK = (1 << 2), 
     NEW_FLAG =  (1 << 3), 
     NEW_FLAG_2 =  (1 << 4), 
     NEW_FLAG_3 =  (1 << 5), 
     NEW_FLAG_4 =  (1 << 6) 
    }; 
}; 
+0

Questo è il modo in cui faccio tutto il tempo in C incorporato ... – drahnr

+0

L'approccio di Jerry che usa 'bit (n)' è un po 'più intuitivo leggendo il codice visivamente, ma il tuo non usa macro. Gah Posso solo scegliere una risposta ... entrambi avete offerto buoni suggerimenti. Qualche idea sul motivo per cui non c'è mai stata una proposta per standardizzare "0b" o qualche forma simile di incarico? –

4

Proprio come una nota, Boost (come al solito) fornisce un implementation di questa idea.

+0

Anche se questo è davvero allettante, Boost non è un C++ ufficiale e ho voglia di trascinarlo nel mio progetto di codice per introdurre possibili restrizioni di dipendenza/piattaforma che non voglio gestire come programmatore a tempo limitato autodidatta con C++. Inoltre, i miei colleghi sono ancora più inesperti del mondo della programmazione di me (ero un CE, quindi almeno avevo lezioni in Java e programmi base con base visiva) quindi ho poca fiducia che sarebbero in grado di mantenere il mio codice boost-driven .... –

+4

Non aver paura delle librerie. Soprattutto Boost, che è * molto * multipiattaforma. –

2

Perché non utilizzare un bitfield struct?

struct preferences { 
     unsigned int likes_ice_cream : 1; 
     unsigned int plays_golf : 1; 
     unsigned int watches_tv : 1; 
     unsigned int reads_stackoverflow : 1; 
    }; 

struct preferences fred; 

fred.likes_ice_cream = 1; 
fred.plays_golf = 0; 
fred.watches_tv = 0; 
fred.reads_stackoverflow = 1; 

if (fred.likes_ice_cream == 1) 
    /* ... */ 
+0

Sto leggendo un valore intero da un file e sto andando a controllarlo contro i flag, usando '&'. Immagino che la tua soluzione * possa * essere adattata a questo, ma sembra meno intuitiva. –

+0

Si potrebbe anche dire che stai leggendo un bitfield a 32 bit da un file. –

1

Cosa c'è di sbagliato con hex per questo caso d'uso?

enum Flags { 
    FLAG_A = 0x00000001, 
    FLAG_B = 0x00000002, 
    FLAG_C = 0x00000004, 
    FLAG_D = 0x00000008, 
    FLAG_E = 0x00000010, 
    // ... 
}; 
2

GCC ha un'estensione che lo rende capace di assegnazione binaria:

int n = 0b01010101; 

Edit: Come di C++ 14, questo è ora una parte ufficiale della lingua.

+0

Suggerirei di apportare modifiche al fatto che a partire da C++ 14 questa è la sintassi ufficiale, non un'estensione. –

+0

@underscore_d grazie, l'ho fatto ora. – Riot

Problemi correlati