Search code examples
c++enumsbit-manipulationbitflagsstd-bitset

Are enums the canonical way to implement bit flags?


Currently I'm using enums to represent a state in a little game experiment. I declare them like so:

namespace State {
  enum Value {
    MoveUp = 1 << 0, // 00001 == 1
    MoveDown = 1 << 1, // 00010 == 2
    MoveLeft = 1 << 2, // 00100 == 4
    MoveRight = 1 << 3, // 01000 == 8
    Still = 1 << 4, // 10000 == 16
    Jump = 1 << 5
  };
}

So that I can use them this way:

State::Value state = State::Value(0);
state = State::Value(state | State::MoveUp);
if (mState & State::MoveUp)
  movement.y -= mPlayerSpeed;

But I'm wondering if this is the right way to implement bit flags. Isn't there a special container for bit flags? I heard about std::bitset, is it what I should use? Do you know something more efficient?
Am I doing it right?


I forgot to point out I was overloading the basic operators of my enum:

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

inline State::Value operator&(State::Value a, State::Value b)
{ return static_cast<State::Value>(static_cast<int>(a) & static_cast<int>(b)); }


inline State::Value& operator|=(State::Value& a, State::Value b)
{ return (State::Value&)((int&)a |= (int)b); }

I had to use a C-style cast for the |=, it didn't work with a static_cast - any idea why?


Solution

  • I believe that your approach is right (except several things):
    1. You can explicitly specify underlying type to save memory;
    2. You can not use unspecified enum values.

    namespace State {
      enum Value : char {
        None      = 0,
        MoveUp    = 1 << 0, // 00001 == 1
        MoveDown  = 1 << 1, // 00010 == 2
        MoveLeft  = 1 << 2, // 00100 == 4
        MoveRight = 1 << 3, // 01000 == 8
        Still     = 1 << 4, // 10000 == 16
        Jump      = 1 << 5
      };
    }
    

    and:

    State::Value state = State::Value::None;
    state = State::Value(state | State::MoveUp);
    if (mState & State::MoveUp) {
      movement.y -= mPlayerSpeed;
    }
    

    about overloading:

    inline State::Value& operator|=(State::Value& a, State::Value b) {
        return a = static_cast<State::Value> (a | b);
    }
    

    and since you use C++11, you should use constexpr every were is possible:

    inline constexpr State::Value operator|(State::Value a, State::Value b) {
        return a = static_cast<State::Value> (a | b);
    }
    
    inline constexpr State::Value operator&(State::Value a, State::Value b) {
        return a = static_cast<State::Value> (a & b);
    }