tags:

views:

751

answers:

6

Treating enums as flags works nicely in C# via the [Flags] attribute, but what's the best way to do this in C++?

For example, I'd like to write:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};

seahawk.flags = CanFly | EatsFish | Endangered;

However, I get compiler errors regarding int/enum conversions. Is there a nicer way to express this than just blunt casting? Preferably, I don't want to rely on constructs from 3rd party libraries such as boost or Qt.

EDIT: As indicated in the answers, I can avoid the compiler error by declaring seahawk.flags as int. However, I'd like to have some mechanism to enforce type safety, so someone can't write seahawk.flags = HasMaximizeButton.

+9  A: 

What type is the seahawk.flags variable?

In standard C++, enumerations are not type-safe. They are effectively integers.

AnimalFlags should NOT be the type of your variable, your variable should be int and the error will go away.

Putting hexidecimal values like some other people suggested is not needed, it makes no difference.

The enum values ARE of type int by default. So you can surely bitwise OR combine them and put them together and store the result in an int.

The enum type is a restricted subset of int who's value is one of it's enumerated values. Hence when you make some new value outside of that range, you can't assign it without casting to a variable of your enum type.

You can also change the enum value types if you'd like, but there is no point for this question.

EDIT: The poster said they were concerned with type safety and they don't want a value that should not exist inside the int type.

But it would be type unsafe to put a value outside of AnimalFlags's range inside a variable of type AnimalFlags.

There is a safe way to check for out of range values though inside the int type...

int iFlags = HasClaws | CanFly;
//InvalidAnimalFlagMaxValue-1 gives you a value of all the bits 
// smaller than itself set to 1
//This check makes sure that no other bits are set.
assert(iFlags & ~(InvalidAnimalFlagMaxValue-1) == 0);



enum AnimalFlags {
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8,

    // put new enum values above here
    InvalidAnimalFlagMaxValue = 16
};

The above doesn't stop you from putting an invalid flag from a different enum that has the value 1,2,4, or 8 though.

If you want absolute type safety then you could simply create an std::set and store each flag inside there. It is not space efficient but it is type safe and gives you the same ability as a bitflag int does.

C++0x note: Strongly typed enums

In C++0x you can finally have type safe enume values....

enum class AnimalFlags {
    CanFly = 2,
    HasClaws = 4
};

if(CanFly == 2) { }//Compiling error
Brian R. Bondy
+1  A: 

use it like thism its bit flags:

enum Styles{
    STYLE1 = 0x1,
    STYLE2 = 0x2,
    STYLE3 = 0x4,
    STYLE4 = 0x8,
    STYLE5 = 0x10,
    STYLE6 = 0x20,
    STYLE7 = 0x40,
    STYLE8 = 0x80
};

BYTE dwStyle = STYLE1 | STYLE3 | STYLE6;    //    [00100101]000

result is

 0b 00000000 00000000 00000000 00000001     <- STYLE1
 0b 00000000 00000000 00000000 00000100     <- STYLE3
 0b 00000000 00000000 00000000 00100000     <- STYLE6
-----------------------------------------------
 0b 00000000 00000000 00000000 00100101     <- STYLE1 | STYLE3 | STYLE6
Kai
A: 

As above(Kai) or do the following. Really enums are "Enumerations", what you want to do is have a set, therefore you should really use stl::set

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};

int main(void)
{
    AnimalFlags seahawk;
    //seahawk= CanFly | EatsFish | Endangered;
    seahawk= static_cast<AnimalFlags>(CanFly | EatsFish | Endangered);
}
Spacen Jasset
+3  A: 

The "correct" way is to define bit operators for the enum, as:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};

inline AnimalFlags operator|(AnimalFlags a, AnimalFlags b)
{return static_cast<AnimalFlags>(static_cast<int>(a) | static_cast<int>(b));}

Etc. rest of the bit operators. Modify as needed if the enum range exceeds int range.

eidolon
+1  A: 

Easiest way to do this as shown here, using the standard library class bitset.

To emulate the C# feature in a type-safe way, you'd have to write a template wrapper around the bitset, replacing the int arguments with an enum given as a type parameter to the template. Something like:

    template <class T, int N>
class FlagSet
{

    bitset<N> bits;

    FlagSet(T enumVal)
    {
     bits.set(enumVal);
    }

    // etc.
};

enum MyFlags
{
    FLAG_ONE,
    FLAG_TWO
};

FlagSet<MyFlags, 2> myFlag;
soru
+1  A: 

If your compiler doesn't support strongly typed enums yet, you can give a look to the following article from the c++ source:

From the abstract:

This article presents a solution to the problem of constraining bit operations to  
allow only safe and legitimate ones, and turn all invalid bit manipulations into
compile-time errors. Best of all, the syntax of bit operations remains unchanged, 
and the code working with bits does not need to be modified, except possibly to 
fix errors that had as yet remained undetected.
Francesco