Ecco un'opzione per le maschere di bit se in realtà non hai un uso per i singoli valori enum (es. Non è necessario disattivarli) ... e se non sei preoccupato di mantenere la compatibilità binaria, ad esempio: tu non importa dove vivono i tuoi pezzi ... che probabilmente sei. Inoltre, è meglio non preoccuparsi troppo di scoping e controllo degli accessi. Hmmm, gli enum hanno delle belle proprietà per i bit-field ... mi chiedo se qualcuno lo abbia mai provato :)
struct AnimalProperties
{
bool HasClaws : 1;
bool CanFly : 1;
bool EatsFish : 1;
bool Endangered : 1;
};
union AnimalDescription
{
AnimalProperties Properties;
int Flags;
};
void TestUnionFlags()
{
AnimalDescription propertiesA;
propertiesA.Properties.CanFly = true;
AnimalDescription propertiesB = propertiesA;
propertiesB.Properties.EatsFish = true;
if( propertiesA.Flags == propertiesB.Flags )
{
cout << "Life is terrible :(";
}
else
{
cout << "Life is great!";
}
AnimalDescription propertiesC = propertiesA;
if( propertiesA.Flags == propertiesC.Flags )
{
cout << "Life is great!";
}
else
{
cout << "Life is terrible :(";
}
}
Possiamo vedere che la vita è fantastica, abbiamo i nostri valori discreti e abbiamo una buona int per & e | al contenuto dei nostri cuori, che ha ancora il contesto di ciò che significano i suoi bit. Tutto è coerente e prevedibile ... per me ... finché continuo a utilizzare il compilatore VC ++ di Microsoft con l'aggiornamento 3 su Win10 x64 e non tocco i miei flag di compilatore :)
Anche se tutto è fantastico ... abbiamo un certo contesto sul significato delle bandiere ora, dal momento che è in un'unione con il campo di battaglia nel terribile mondo reale in cui il tuo programma potrebbe essere responsabile di più di un singolo compito discreto che potresti ancora accidentalmente (abbastanza facilmente) rompono insieme due campi di bandiere di diversi sindacati (diciamo, AnimalProperties e ObjectProperties, poiché sono entrambi ints), mescolando tutti i tuoi bit, che è un orribile bug da rintracciare ... e come lo so molte persone in questo post non lavorano molto spesso con le maschere di bit, poiché costruirle è facile e mantenerle è difficile.
class AnimalDefinition {
public:
static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags ); //A little too obvious for my taste... NEXT!
static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties ); //Oh I see how to use this! BORING, NEXT!
static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something?
AnimalFlags animalFlags; //Well this is *way* too hard to break unintentionally, screw this!
int flags; //PERFECT! Nothing will ever go wrong here...
//wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation?
//Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew!
private:
AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :(
}
Quindi rendi la tua dichiarazione sindacale privata per impedire l'accesso diretto alle "Bandiere", e devi aggiungere getter / setter e sovraccarichi dell'operatore, quindi creare una macro per tutto ciò e sei praticamente tornato da dove hai iniziato quando hai provato a fallo con un Enum.
Sfortunatamente se vuoi che il tuo codice sia portatile, non penso che ci sia modo di A) garantire il layout dei bit o B) determinare il layout dei bit al momento della compilazione (in modo da poterlo tracciare e almeno correggere per le modifiche in versioni / piattaforme ecc.)
Offset in una struttura con campi di bit
In fase di esecuzione puoi giocare brutti scherzi con l'impostazione dei campi e XORing le bandiere per vedere quali bit sono cambiati, mi sembra piuttosto schifoso anche se i versi hanno una soluzione coerente al 100%, indipendente dalla piattaforma e completamente deterministica, ovvero: un ENUM.
TL; DR: non ascoltare chi odia. C ++ non è inglese. Solo perché la definizione letterale di una parola chiave abbreviata ereditata da C potrebbe non adattarsi al tuo utilizzo non significa che non dovresti usarla quando C e definizione C ++ della parola chiave include assolutamente il tuo caso d'uso. Puoi anche usare le strutture per modellare cose diverse dalle strutture e classi per cose diverse dalla scuola e dalla casta sociale. È possibile utilizzare float per i valori che sono radicati. Puoi usare char per variabili che non sono né bruciate né una persona in un romanzo, in una commedia o in un film. Qualsiasi programmatore che va nel dizionario per determinare il significato di una parola chiave prima che la specifica della lingua sia ... beh, terrò la mia lingua lì.
Se vuoi che il tuo codice sia modellato sulla lingua parlata, ti conviene scrivere in Objective-C, che per inciso usa anche enumerazioni pesanti per bitfield.
[Flags]
attributo funziona bene, vale a dire:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};