Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 855
Yohaï-Eliel Berreby Avatar asked Jun 16 '14 20:06

Yohaï-Eliel Berreby


People also ask

What is flagged enum?

A flagged enum can be used to efficiently send and store a collection of boolean values. In a flagged enum, each value of the enum is assigned to a bit value. These must be bit values because each combination possible will be unique.

What are bit flags and enum sets in Java?

Using Bit Flags and EnumSets in Java. 20 Dec 2013. Bit flags, commonly referred to as Bit fields are an efficient way of storing several related boolean values in a single primitive type. Internally represented in binary, you can decide on how large the storage type needs to be - for example, a Java integer will provide you with space for 31 flags.

How many flags can an enum have?

Therefore, if an enum is meant to accomodate a bitset of more than 32 flags, you need to specify a bigger type explicitely: Although flags are often only a single bit, they can be combined into named "sets" for easier use.

Why can’t I use flagsattribute with enumeration constants?

Because FlagsAttribute relies on the enumeration constants to be powers of two (or their combinations) and enum values are ultimately numeric values, you are limited by the size of the underlying numeric type. The largest available numeric type that you can use is UInt64, which allows you to specify 64 distinct (non-combined) flag enum constants.

Can an enum have more than 32 different labels?

Lastly, since enums are normally stored into , it’s unwise to have an enum with more then 32 different labels. . The only thing it does is allowing a nicer output of enums when they are printed. A bit mask is, essentially, an integer value in which several binary property (yes/no) are independently stored in its bit.


1 Answers

The STL contains std::bitset, which you can use for precisely such a case.

Here is just enough code to illustrate the concept:

#include <iostream>
#include <bitset>

class State{
public:
    //Observer
    std::string ToString() const { return state_.to_string();};
    //Getters
    bool MoveUp()    const{ return state_[0];}; 
    bool MoveDown()  const{ return state_[1];}; 
    bool MoveLeft()  const{ return state_[2];}; 
    bool MoveRight() const{ return state_[3];}; 
    bool Still()     const{ return state_[4];}; 
    bool Jump()      const{ return state_[5];}; 
    //Setters
    void MoveUp(bool on)    {state_[0] = on;}
    void MoveDown(bool on)  {state_[1] = on;}
    void MoveLeft(bool on)  {state_[2] = on;}
    void MoveRight(bool on) {state_[3] = on;}
    void Still(bool on)     {state_[4] = on;}
    void Jump(bool on)      {state_[5] = on;}
private:
    std::bitset<6> state_;
};


int main() {
    State s;
    auto report = [&s](std::string const& msg){
        std::cout<<msg<<" "<<s.ToString()<<std::endl;
    };
    report("initial value");
    s.MoveUp(true);
    report("move up set");
    s.MoveDown(true);
    report("move down set");
    s.MoveLeft(true);
    report("move left set");
    s.MoveRight(true);
    report("move right set");
    s.Still(true);
    report("still set");
    s.Jump(true);
    report("jump set");
    return 0;
}

Here's it working: http://ideone.com/XLsj4f

The interesting thing about this is that you get std::hash support for free, which is typically one of the things you would need when using state inside various data structures.

EDIT: There is one limitation to std::bitset and that is the fact that you need to know the maximum number of bits in your bitset at compile time. However, that is the same case with enums anyway.

However, if you don't know the size of your bitset at compile time, you can use boost::dynamic_bitset, which according to this paper (see page 5) is actually really fast. Finally, according to Herb Sutter, std::bitset was designed to be used in cases you would normally want to use std::vector.

That said, there really is no substitute for real world tests. So if you really want to know, profile. That will give you performance numbers for a context that you care about.

I should also mention that std::bitset has an advantage that enum does not - there is no upper limit on the number of bits you can use. So std::bitset<1000> is perfectly valid.

like image 98
Carl Avatar answered Sep 28 '22 02:09

Carl