Narek
Narek

Reputation: 39871

How to use C++11 enum class for flags

Say I have such a class:

enum class Flags : char
{
    FLAG_1 = 1;
    FLAG_2 = 2;
    FLAG_3 = 4;
    FLAG_4 = 8;
};

Now can I have a variable that has type flags and assign a value 7 for example? Can I do this:

Flags f = Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3;

or

Flags f = 7;

This question arises because in the enum I have not defined value for 7.

Upvotes: 23

Views: 26718

Answers (8)

bobobobo
bobobobo

Reputation: 67244

Use enum class only for enums that are distinct states and cannot be bitwise combined

Don't use enum class for enums that you intend to use as bitwise combineable enums

Operator overloading your enum class is really code bloat is not sensible to be doing when enum provides that by default

If you're experiencing name collisions, you can use very long names for your enum:

enum ColorComponent {
  ColorComponentNone = 0,
  ColorComponentRed    = 1 << 0,
  ColorComponentGreen  = 1 << 1,
  ColorComponentBlue   = 1 << 2,
};

Use like:

ColorComponent cc = (ColorComponent)(ColorComponentRed | ColorComponentBlue);

Another way to avoid name collisions is to use this classic trick of namespaceing the enum:

namespace ColorComponent {
  enum Enum {
    None   = 0,
    Red    = 1 << 0,
    Green  = 1 << 1,
    Blue   = 1 << 2,
  };
};

You can use that enum like:

ColorComponent::Enum e = (ColorComponent::Enum)(ColorComponent::Red | ColorComponent::Blue);

Consider also safety issues in Why is enum class considered safer to use than plain enum?

If you can handle the idea that your enum implicitly converts to int, you shouldn't use enum class for bitwise combinable enums

If you completely insist on using enum class because your brain is locked in a gear that says "MUST USE C++ NEW enum class FEATURE", then you should probably save yourself a lot of code repeat and use a macro to operator overload your enum class that want bitwise ops defined for them

Upvotes: 0

Questionable
Questionable

Reputation: 878

I realize this question is a bit old, but I will write out a method I have used to do this.

(If anything, if I Google this again in the future I have it documented to find again.)

Personally I like this method because intellisense (at least the VSCode version of it... I don't have Visual Studio on Linux...) will automatically pick up on what you're doing and give you useful hints. Also, it avoids the use of macros, so the compiler can warn you if it is not happy. Lastly, without the comments, it isn't a lot of code. You're not making a class and setting a bunch of overloads or anything, but you're still getting the benefit of scoped enums so that you can reuse a flag name/value for another enum. Anyway onto the example.

namespace FlagsNS
{
    /* This is an old/classic style enum so put it in a
    namespace so that the names don't clash
    (ie: you can define another enum with the values of 
    Flag_1 or Flag_2, etc... without it blowing up)
    */
    enum Flags
    {
        Flag_1 = 1 << 0, //Same as 1
        Flag_2 = 1 << 1, //Same as 2
        Flag_3 = 1 << 2, //Same as 4
        Flag_4 = 1 << 3 //Same as 8
    };
}
/* This is telling the compiler you want a new "type" called Flags
but it is actually FlagsNS::Flags. This is sort of like using the
#define macro, except it doesn't use the preprocessor so the
compiler can give you warnings and errors.
*/
using Flags = FlagsNS::Flags;

//Later in code.... so int main() for example
int main()
{
    //If you don't mind c-style casting
    Flags flag = (Flags)(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);
    //Or if you want to use c++ style casting
    Flags flag = static_cast<Flags>(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);

    //Check to see if flag has the FLAG_1 flag set.
    if (flag & Flags::FLAG_1)
    {
        //This code works
    }
}

Upvotes: 3

Oleksa
Oleksa

Reputation: 675

It should handle any enumeration type. I'm not sure it doesn't have any side effects and is completely valid C++ code. Let me know if there are some issues.

template<class T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
constexpr T operator|(T lhs, T rhs) 
{
    return static_cast<T>(
        static_cast<std::underlying_type<T>::type>(lhs) | 
        static_cast<std::underlying_type<T>::type>(rhs));
}

Upvotes: 5

NutCracker
NutCracker

Reputation: 12263

It's maybe better to make use of std::underlying_type instead of hard-coding char type.

Flags operator|(Flags lhs, Flags rhs) {
    return static_cast<Flags>(
        static_cast<std::underlying_type<Flags>::type>(lhs) |
        static_cast<std::underlying_type<Flags>::type>(rhs)
    );
}

Now, you can change the underlying type of your enumeration without needing to update that type in every bitwise operator overload.

Upvotes: 18

RamblingMad
RamblingMad

Reputation: 5518

Please don't do this. If you need to do this, enum classs probably aren't what you need.

@T.C. showed you how to do it so long as you specify underlying type, but you will run into places where your program does things it just shouldn't.

An example is where you use a switch and have a case for every defined enum value.

e.g.

enum class my_enum: unsigned int{
    first = 1,
    second = 2,
    third = 4,
    fourth = 8
};

int main(){
    auto e = static_cast<my_enum>(static_cast<unsigned int>(my_enum::first) | static_cast<unsigned int>(my_enum::second));

    switch(e){
        case my_enum::first:
        case my_enum::second:
        case my_enum::third:
        case my_enum::fourth:
            return 0;
    }

    std::cout << "Oh, no! You reached a part of the program you weren't meant to!\n";
    return 1;
}

Will output:

Oh, no! You reached a part of the program you weren't meant to!

then return the error code 1.

Which is also an example of why you should always have a default case, of course, but that isn't my point.

Of course, you could argue that so long as the user of the enum class never directly uses the value other than passing to a function; it would be a good way of restricting the values of a bitset. But I've always been a little too trustworthy and find std::uint[n]_t and some constexpr variables the best way (if a user sets an invalid bit it simply does nothing).

What you're doing just isn't really suitable for enum class, because it defeats the purpose of having a scoped enumeration. You can no longer enumerate the values if you set it to an undefined one.

Upvotes: 2

T.C.
T.C.

Reputation: 137315

You need to write your own overloaded operator| (and presumably operator& etc.).

Flags operator|(Flags lhs, Flags rhs) 
{
    return static_cast<Flags>(static_cast<char>(lhs) | static_cast<char>(rhs));
}

Conversion of an integer to an enumeration type (scoped or not) is well-defined as long as the value is within the range of enumeration values (and UB otherwise; [expr.static.cast]/p10). For enums with fixed underlying types (this includes all scoped enums; [dcl.enum]/p5), the range of enumeration values is the same as the range of values of the underlying type ([dcl.enum]/p8). The rules are trickier if the underlying type is not fixed - so don't do it :)

Upvotes: 30

nishantjr
nishantjr

Reputation: 1820

At this point, It probably makes sense to define your own class to handle this.

 /** Warning: Untested code **/
 struct Flag {

     static Flag Flag_1;
     static Flag Flag_2;
     static Flag Flag_3;
     static Flag Flag_4;

     Flag operator = (Flag);
 private:
     char const value;
 };

 Flag operator | (Flag, Flag); 

Upvotes: -1

CinCout
CinCout

Reputation: 9619

The code in question doesn't compile. But you can do something like this,

enum class Flags : char
{
    FLAG_1 = 1,
    FLAG_2 = 2,
    FLAG_3 = 4,
    FLAG_4 = 8,
};

int main() {
    Flags f = static_cast<Flags>(7);
    Flags f1 = static_cast<Flags>( static_cast<char>(Flags::FLAG_1) | static_cast<char>(Flags::FLAG_2) | static_cast<char>(Flags::FLAG_3) );
    return 0;
}

and it works

Upvotes: 0

Related Questions