Using enum classes as type-safe bitmasks

This write-up is (to the largest part) a reiteration of a blog post by Anthony Williams.

Intro

In C++, bitmasks are often represented by either

  • preprocessor defines
#define Readable   0x4
#define Writeable  0x2
#define Executable 0x1

unsigned permissions = Readable | Executable;  
  • integer constants
static const unsigned Readable   = 0x4;  
static const unsigned Writable   = 0x2;  
static const unsigned Executable = 0x1;

unsigned permissions = Readable | Executable;  
  • or plain enums
enum Permissions  
{
    Readable = 0x4, Writable = 0x2, Executable = 0x1
};

Permissions permissions = static_cast<Permissions>(Readable | Executable);  

All of these three approaches have inherent type safety problems. We will see how we can implement bitmasks in a type-safe way using C++11 enum classes.

A motivating example

With the preprocessor and the integer constant approach nothing prevents us from shooting ourselves in the foot by freely mixing our flags with arbitrary values:

unsigned permissions = Readable * moonPhase;    // hmmh ...  

Using plain enums doesn't buy us much though, as we see in the next example:

enum Permissions {Readable = 0x4, Writable = 0x2, Executable = 0x1};  
enum Taste {Sweet, Sour, Salty, Bitter, Umami};

// Plain enums implicitly convert to int, so we can use bitwise operators
Permissions perms = static_cast<Permissions>(Readable | Writeable);

// But we do not want all operators that are allowed on integers
perms = static_cast<Permissions>(Readable * Writeable / Executable); // What the heck???

// Not type-safe due to conversions to int
perms = static_cast<Permissions>(Executable | Salty); // Huh???  
perms = static_cast<Permissions>(Executable ^ 42); // umm, what does that mean?  

Plain enums implicitly convert to integers but not vice versa. Since they convert to int, we can use bitwise operators |,&,^,~. But we need to cast the return value of the operators back to the plain enum as in Permission permissions = static_cast<Permissions>(Readable | Writable);.

A big drawback is though, that the conversion to int pulls in all the operators
defined for int. We should rather not provide most of them for a bitmask type.

And again, we can smuggle in arbitrary values into our bitmasks, which is not what we want.

What do we want then?

We want bitmasks which we can use in a type-safe manner like this:

Permissions permissions = Permissions::Readable | Permissions::Writable; // ok  
permissions = permissions | Permissions::Executable; // ok  
permissions &= ~Permissions::Writeable; // ok

permissions = Premissions::Readable * Permissions::Executable; // error  
permisisons = Permissions::Readable | 42;   // error  
permissions = SomeOtherThingy::Readable     // error  

We want them to not implicitly decay to integers. We only want to allow the necessary operations on them, namely bitwise |, &, ^, ~, assigment and modifiers =, |=, &=, ^=. And of course we'd rather prevent arbitrary values from creeping in.

C++11 enum classes to the rescue

As it turns out, we can use C++11 to implement our type-safe bitmasks. Strongly typed enums, a.k.a enum classes can help us here.

enum class Permissions : unsigned {Readable = 0x4, Writable = 0x2, Executable = 0x1};

// does not compile yet
Permissions perms = Permissions::Readable | Permissions::Writable;  

Since enum classes do not implicitly convert to integers, the above code does not compile.
A bitwise | operator is missing, so we have to define our own operator |.

Permissions operator |(Permissions lhs, Permissions rhs)  
{
    return static_cast<Permissions> (
        static_cast<unsigned>(lhs) |
        static_cast<unsigned>(rhs)
    );
}    

Or, better yet as it would correctly cover the enum's underlying type:

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

The same has to be done for all the other operators &, ^, ~, |=, &=, ^=.

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

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

Permissions operator ~(Permissions rhs)  
{
    return static_cast<Permissions> (
        ~static_cast<std::underlying_type<Permissions>::type>(rhs)
    );
}

Permissions& operator |=(Permissions &lhs, Permissions rhs)  
{
    lhs = static_cast<Permissions> (
        static_cast<std::underlying_type<Permissions>::type>(lhs) |
        static_cast<std::underlying_type<Permissions>::type>(rhs)           
    );

    return lhs;
}

Permissions& operator &=(Permissions &lhs, Permissions rhs)  
{
    lhs = static_cast<Permissions> (
        static_cast<std::underlying_type<Permissions>::type>(lhs) &
        static_cast<std::underlying_type<Permissions>::type>(rhs)           
    );

    return lhs;
}

Permissions& operator ^=(Permissions &lhs, Permissions rhs)  
{
    lhs = static_cast<Permissions> (
        static_cast<std::underlying_type<Permissions>::type>(lhs) 
        static_cast<std::underlying_type<Permissions>::type>(rhs)           
    );

    return lhs;
}

Now we can use our bitmasks like intended:

Permissions perms = Permissions::Readable | Permissions::Writable;  

Reducing boilerplate code

"Eeek! So much boilerplate code only for those trivial operators!"

i hear you say,

"Okay, we are type-safe now, but at what cost!"

And you are absolutely right!

So let's stop hyperventilating and see, what we can do about this.

Luckily the implementation is the same for every bitmask type so we can use templates to write those operators only once:

template<typename Enum>  
Enum operator |(Enum lhs, Enum rhs)  
{
    static_assert(std::is_enum<Enum>::value, 
                  "template parameter is not an enum type");

    using underlying = typename std::underlying_type<Enum>::type;

    return static_cast<Enum> (
        static_cast<underlying>(lhs) |
        static_cast<underlying>(rhs)
    );
}

SFINAE, f**k yeah

The above approach still has a major glitch. The template is a greedy beast. It catches all enuns, not only those we intend to use as bitmasks. This might not always be sensible.

enum class Thingy {Pencil, Football, Mixer, Bed};

Thingy thing = Thingy::Football | Thingy::Mixer & ~Thingy::Pencil;  // errm, excuse me Sir?  

Since our templated operator | kicks in here, it is possible to do
a bitwise or of these enumeration values. That doesn't make a lot of sense in this case.

It would be better to enable the operators explicitly. And we can do so by using std::enable_if:

template<typename Enum>  
struct EnableBitMaskOperators  
{
    static const bool enable = false;
};

template<typename Enum>  
typename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type  
operator |(Enum lhs, Enum rhs)  
{
    using underlying = typename std::underlying_type<Enum>::type;
    return static_cast<Enum> (
        static_cast<underlying>(lhs) |
        static_cast<underlying>(rhs)
    );
}

To enable the bitwise operators, we have to provide a template specialisation for our bitmask enum:

template<>  
struct EnableBitMaskOperators<Permissions>  
{
    static const bool enable = true;
};

That looks a bit too verbose, so we put it into a macro to make it a oneliner:

#define ENABLE_BITMASK_OPERATORS(x)  \
template<>                           \  
struct EnableBitMaskOperators<x>     \  
{                                    \
    static const bool enable = true; \
};

Now we can safely use our bitmasks like this:

enum class Permissions  
{
    Readable   = 0x4,
    Writeable  = 0x2,
    Executable = 0x1
};
ENABLE_BITMASK_OPERATORS(Permissions)

// all ok
Permissions p = Permissions::Readable | Permissions::Writable;  
p |= Permissions::Executable;  
p &= ~Permissions::Writable;

enum class Color {Red, Green, Blue};

// compilation fails, as intended
Color yellow = Color::Red | Color::Green;  

Conclusion

We saw that is possible to make your bitmasks type-safe with only little
overhead in code size (the ENABLE_BITMASK_OPERATORS macro is needed).

If you have types that represent pure bitmasks, this approach is absolutely feasible.

Andre Haupt

Read more posts by this author.