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?
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?
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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With