How to make functions with flag parameters? (C++)
Asked Answered
R

6

12

How could I make a function with flags like how Windows' CreateWindow(...style | style,...), for example, a createnum function:

int CreateNum(flag flags) //???
{
    int num = 0;
    if(flags == GREATER_THAN_TEN)
        num = 11;
    if(flags == EVEN && ((num % 2) == 1)
        num++;
    else if(flags == ODD && ((num % 2) == 0)
        num++;
    return num;
}
//called like this
int Number = CreateNum(GREATER_THAN_TEN | EVEN);

Is this possible, and if so, how?

Renita answered 19/10, 2009 at 15:7 Comment(0)
T
34

You can define an enum specifying "single bit" values (note that the enclosing struct is acting here only as a naming context, so that you can write e.g. MyFlags::EVEN):

struct MyFlags{
    enum Value{
        EVEN                           = 0x01,
        ODD                            = 0x02,
        ANOTHER_FLAG                   = 0x04,
        YET_ANOTHER_FLAG               = 0x08,
        SOMETHING_ELSE                 = 0x10,
        SOMETHING_COMPLETELY_DIFFERENT = 0x20
    };
};

and then use it like this:

int CreateNum(MyFlags::Value flags){
    if (flags & MyFlags::EVEN){
        // do something...
    }
}

void main(){
    CreateNum((MyFlags::Value)(MyFlags::EVEN | MyFlags::ODD));
}

or simply like this:

int CreateNum(int flags){
    if (flags & MyFlags::EVEN){
        // do something...
    }
}

void main(){
    CreateNum(MyFlags::EVEN | MyFlags::ODD);
}

You could also simply declare integer constants, but the enum is clearer in my opinion.

Note: I updated the post to take some comments into account, thanks!

Toreutic answered 19/10, 2009 at 15:11 Comment(9)
Maybe you could make your enum longer, to make clear it HAS to be powers of two ?Guadalupeguadeloupe
+1. as an aside, it is unnecessary to compare the flag with "== MyFlags::EVEN", since it will be either zero or non-zero, which automatically coerces to bool rather nicely.Biddick
I don't think it is possible to pass two (or more) flags using | operator if you use this approach.Macneil
Why couldn't you pass multiple flags this way? looks like you couldKrusche
Because EVEN | ODD will get converted to int, and CreateNum() accepts MyFlags::Value. I won't compile IMO, but I could be wrong.Macneil
danadam is right : the parameter shouldn't be 'MyFlags::Value', because a combination of flags is not a MyFlags::Value (for example EVEN | ODD gives 3, which is not a valid enum member). You can still cast the combination, but it's dirty.Guadalupeguadeloupe
Small tip: I find it nicer to write enum values as: 1 << 0, 1 << 1, 1 << 2, etc, so I don't make silly mistakes writing power-of-two literally. (I seldom do, but I feel very silly when it happens, so I try to avoid it).Schleiermacher
@Marcus: it seems to me that there's the same probability of making a mistake with the 2 notations, don't you think :) ?Toreutic
@Macneil - I would have done something like default ODD, so it wouldn't be possible to say EVEN | ODD, just EVEN if you want the number to be even, and nothing for odd.Renita
R
15

I upvoted orsogufo's answer, but I always liked doing the following for defining the values:

enum Value{
  EVEN                           = (1<<0),
  ODD                            = (1<<2),        
  ANOTHER_FLAG                   = (1<<3),        
  YET_ANOTHER_FLAG               = (1<<4),        
  SOMETHING_ELSE                 = (1<<5),        
  SOMETHING_COMPLETELY_DIFFERENT = (1<<6),

  ANOTHER_EVEN                   = EVEN|ANOTHER_FLAG
};

<< is the shift operator. Incrementing the right side lets you generate sequential bit masks by moving the 1 over, one bit at a time. This has the same values for the bare flags, but reads easier to my eyes and makes it obvious if you skip or duplicate a value.

I also like combining some common flag combinations when appropriate.

Reversal answered 19/10, 2009 at 15:43 Comment(2)
Great solution. The introduction of logical shifts makes it a much easier to read update.Terrain
What about 1<<1 ? :)Exteroceptor
S
6

You can use const int like this:

const int FLAG1 = 0x0001;
const int FLAG2 = 0x0010;
const int FLAG3 = 0x0100;
// ...

And when you use it:

int CreateNum(int flags)
{
    if( flags & FLAG1 )
        // FLAG1 is present

    if( flags & FLAG2 )
        // FLAG2 is present

    // ...
}

Of course you can put one or more flag in your flags using the | operator.

Spaniard answered 19/10, 2009 at 15:12 Comment(3)
Change #define FLAG1 0x0001 to const int FLAG1=0x0001;Fustigate
agree with Alexey... don't use macros for this.Tugboat
const int can be replaced with constexpr int nowadays.Bel
T
2

Use powers of two as the individual constants, like

enum Flags { EVEN = 0x1, ODD = 0x2, GREATER_TEN = 0x4 };

and you use the logical and operator '&' for testing, like

if( flags & GREATER_THAN_TEN)
    num = 11;
if( (flags & EVEN) && (num % 2) == 1 )
    num++;
else if ( (flags & ODD) && (num % 2) == 0 )
    num++;
return num;
Towney answered 19/10, 2009 at 15:16 Comment(0)
T
0

You've got your tests wrong. What you want is something like (flags & EVEN), where EVEN is an integer with a single bit set (1, 2, 4, 8, 16 - some power of 2). (The integer can be an int or an enum. You could have a macro, but that's generally not a good idea.)

You can use the notation you listed, by overloading flags::operator==(flagvalue f), but it's a bad idea.

Tripedal answered 19/10, 2009 at 15:13 Comment(0)
P
0
enum flags {
    EVEN =        0x0100,
    ODD =         0x0200,
    BELOW_TEN =   0x0400,
    ABOVETEN =    0x0800,
    HUNDRED =     0x1000,
    MASK =        0xff00
};

void some_func(int id_and_flags)
{
    int the_id = id_and_flags & ~MASK;
    int flags = id_and_flags & MASK;
    if ((flags & EVEN) && (the_id % 2) == 1)
        ++the_id;
    if ((flags & ODD) && (the_id % 2) == 0)
        ++the_id;
    // etc
}

Illustrates masking of bit fields too which can be useful when you just need to bolt on a simplistic bit of extra functionality without adding any extra data structure.

Pilatus answered 19/10, 2009 at 15:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.