Aqui está uma opção para máscaras de bits se você realmente não usar os valores de enum individuais (por exemplo, não precisa desativá-los) ... e se você não estiver preocupado em manter a compatibilidade binária, por exemplo: não importa onde moram seus bits ... o que você provavelmente está. Além disso, é melhor você não se preocupar muito com o escopo e o controle de acesso. Hmmm, enums têm boas propriedades para campos de bits ... pergunto se alguém já tentou isso :)
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 :(";
}
}
Podemos ver que a vida é ótima, temos nossos valores discretos e temos uma boa intenção de & e | ao conteúdo de nossos corações, que ainda tem contexto do que seus bits significam. Tudo é consistente e previsível ... para mim ... desde que eu continue usando o compilador VC ++ da Microsoft com a Atualização 3 no Win10 x64 e não toque nos sinalizadores do meu compilador :)
Mesmo que tudo esteja ótimo ... temos algum contexto quanto ao significado das bandeiras agora, pois elas estão em uma união com o campo de bits no terrível mundo real, onde seu programa pode ser responsável por mais do que uma tarefa discreta que você poderia ainda acidentalmente (com muita facilidade) esmague dois campos de bandeiras de diferentes uniões (digamos, AnimalProperties e ObjectProperties, já que ambos são ints), misturando todos os seus bits, o que é um bug horrível para rastrear ... e como eu sei muitas pessoas neste post não trabalham com máscaras de bits com muita frequência, pois construí-las é fácil e mantê-las difíceis.
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 :(
}
Então, você torna sua declaração de união privada para impedir o acesso direto a "Flags", e precisa adicionar getters / setters e sobrecargas de operador, criar uma macro para tudo isso e você está basicamente no ponto em que começou quando tentou faça isso com um Enum.
Infelizmente, se você deseja que seu código seja portátil, acho que não há como A) garantir o layout de bits ou B) determinar o layout de bits no tempo de compilação (para que você possa rastreá-lo e, pelo menos, corrigir as alterações) versões / plataformas etc)
Deslocamento em uma estrutura com campos de bits
Em tempo de execução, você pode executar truques com a definição dos campos e o XOR com as bandeiras para ver quais bits foram alterados, para mim parece bastante ruim, embora os versos tenham uma solução 100% consistente, independente da plataforma e completamente determinística, ou seja: um ENUM.
TL; DR: Não dê ouvidos aos inimigos. C ++ não é inglês. Só porque a definição literal de uma palavra-chave abreviada herdada de C pode não se adequar ao seu uso não significa que você não deve usá-la quando C e definição em C ++ da palavra-chave incluir absolutamente seu caso de uso. Você também pode usar estruturas para modelar outras coisas que não sejam estruturas e classes para outras que não sejam a escola e a casta social. Você pode usar float para valores aterrados. Você pode usar char para variáveis que não são queimadas nem uma pessoa em um romance, peça ou filme. Qualquer programador que for ao dicionário para determinar o significado de uma palavra-chave antes da especificação do idioma é um ... bem, eu vou segurar minha língua lá.
Se você deseja que seu código seja modelado após a linguagem falada, é melhor escrever no Objective-C, que aliás também usa enumerações pesadamente para campos de bits.
[Flags]
atributo funciona muito bem, ou seja:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};