tags:

views:

554

answers:

7
+2  Q: 

Flags, enum (C)

I'm not very used to programming with flags, but I think I just found a situation where they'd be useful:

I've got a couple of objects that register themselves as listeners to certain events. What events they register for is dependent on a variable that is sent to them when they are constructed. I think a nice way to do this would be to send bitwise OR connected variables, like such: TAKES_DAMAGE | GRABBABLE | LIQUID, etc. Then, in the constructor, the object can check what flags are set and register it self as listener for the ones that are.

But this is where I get confused. Preferably, the flags would be in an enum. But that is also a problem. If we have got these flags:

enum
{
    TAKES_DAMAGE,/* (0) */
    GRABBABLE, /* (1) */
    LIQUID, /* (2) */
    SOME_OTHER /* (3) */
};

Then sending the flag SOME_OTHER (3) will be the same as sending GRABBABLE | LIQUID, will it not?

How exactly do you deal with this stuff?

+3  A: 

You should make the flags only powers of two, i.e. each is a bit in whatever data type you're storing this in, and nothing overlaps when you bitwise OR.

Jefromi
+18  A: 

Your enumeration needs to be powers of two :

enum
{
    TAKES_DAMAGE = 1,
    GRABBABLE = 2,
    LIQUID = 4,
    SOME_OTHER = 8
};

Or in a more readable fashion :

enum
{
    TAKES_DAMAGE = 1 << 0,
    GRABBABLE = 1 << 1,
    LIQUID = 1 << 2,
    SOME_OTHER = 1 << 3
};

Why ? Because you want to be able to combine flags with no overlapping, and also be able to do this:

if(myVar & GRABBABLE)
{
    // grabbable code
}

... Which works if the enumeration values look like this :

 TAKES_DAMAGE: 00000001
 GRABBABLE:    00000010
 LIQUID:       00000100
 SOME_OTHER:   00001000

So, say you've set myVar to GRABBABLE | TAKES_DAMAGE, here's how it works when you need to check for the GRABBABLE flag:

 myVar:     00000011
 GRABBABLE: 00000010 [AND]
 -------------------
            00000010 // non-zero => converts to true

If you'd set myVar to LIQUID | SOME_OTHER, the operation would have resulted in :

 myVar:     00001100
 GRABBABLE: 00000010 [AND]
 -------------------
            00000000 // zero => converts to false
Samuel_xL
nice twist re: shit operator!
jldupont
@jldupont - I trust you meant "shiFt operator"? 8v)
Fred Larson
@jldupont I think you're missing a letter...
Greg
Don't think I've gotten 5 replies simultaneously before. o.oThanks for the excellent explanation and solution.
quano
Hooray! Now I have a new way to talk about C++ stream insertion and extraction: `cout << "that stinks!";`
Michael Burr
if you dislike the shift operator method, it might be preferable to use hexadecimal rather than decimal: TAKES_DAMAGE=0x0001, GRABBABLE=0x0002, LIQUID=0x0004, GAS=0x0008, PUSHABLE=0x0010, INFLICTS_DAMAGE=0x0020, OTHER=0x0040 etc. That way it keeps looking pretty with all values aligned in code.
James Morris
+2  A: 

you need

enum
{
    TAKES_DAMAGE = 1,
    GRABBABLE = 2,
    LIQUID = 4,
    SOME_OTHER = 8
};
Fredou
I think you need to fix the syntax on this one. (I know, the same error was in the question. That's been fixed now, though.)
Fred Larson
@fred larson, fixed :-)
Fredou
+2  A: 

Can't you just set the values in the enum?

enum {
 TAKES_DAMAGE = 1,
 GRABBABLE    = 2,
 LIQUID       = 4
}

Afterwards, just perfom bit-wise OR on them.

jldupont
+2  A: 

Yes. Instead, make your enum members powers of 2:

enum
{
    TAKES_DAMAGE = (1 << 0),
    GRABBABLE = (1 << 1),
    LIQUID = (1 << 2),
    SOME_OTHER = (1 << 3)
};
Fred Larson
To the downvoter: perhaps you'd like to explain what you found not useful about this answer?
Fred Larson
+6  A: 

another way of storing flags is to not bother with the underlying type at all. when using an enum, the enum values are stored by default into an unsigned int, which is 32 bits on a common computer. this gives you with only 32 possible flags: while certainly much, there are some cases where it is not sufficient.

now you can define your flag set this way:

typedef struct
{
    int takes_damage : 1;
    int grabbable    : 1;
    int liquid       : 1;
    int some_other   : 1;
} flags;

if you never encountered this, the ': 1' part tells the compiler to only use 1 bit to store this struct member.

now you can define a variable to hold the flags, and work with those flags:

flags myflags = {1,0,0,1}; // defines a variable holding a set of flags, with an initial value of takes_damage & some_other

myflags.liquid = 1; // change the flags to include the liquid

if ( myflags.takes_damage ) // test for one flag
    apply_damage();
if ( myflags.liquid && myflags.some_other ) // test for multiple flags
    show_strange_behavior();

this method allows you to define any number of flags, without limitation, and you can extend your flag set at any time without fearing an overflow. the drawback is that testing a subset of the flags is more cumbersome and necessitate more code.

Adrien Plisson
A: 

but if you are only using powers of 2, does that not limit you to 8 enum flags? Can you use a datatype like Int and still perform the same compair process of (flag & flag.value)==flag.value;

?

jim