Flags on steroids

In game development, C++ enums are often used to denote that certain values can be combined using bitwise-operations, resulting in so-called flags. However, enums in pre-C++0x exhibit some drawbacks which will be discussed and improved upon in this post.

Let’s start with a simple example used for specifying different file open modes:

enum Mode
{
  MODE_READ = 1 << 0,
  MODE_WRITE = 1 << 1,
  MODE_APPEND = 1 << 2,
  MODE_RANDOM_ACCESS = 1 << 3
};

Each of the values in the enum corresponds to exactly one bit, hence those values can be combined using bitwise-OR:

int mode = MODE_READ | MODE_WRITE | MODE_RANDOM_ACCESS;

Testing whether a certain mode is set is simple:

const bool isReadMode = ((mode & MODE_READ) != 0);

So far, so good. However, as soon as we start passing such values to functions, the first problem arises: of which type should the argument be?

Suppose we want to write a simple function for opening files:

void Open(? mode) // which type?
{
  // open a file using the OS
}

There’s two choices for the type of mode – we can either use Mode or an integer. Both have their own share of problems, however. Observe:

void Open(Mode mode)
{
  // open a file using the OS
}

Open(MODE_READ | MODE_WRITE); // doesn't compile, not of type Mode
Open((Mode)(MODE_READ | MODE_WRITE)); // compiles

Even though the second example compiles, we’ve just entered the realm of undefined/unspecified/implementation-defined C++ behaviour, because MODE_READ | MODE_WRITE (=3) is not a valid enum value, and hence shouldn’t be cast as such. Additionally, the burden of casting now rests upon the clients using the code, which is ugly.

If we were to use an integer as argument type instead, we run into a completely different set of problems:

void Open(int mode)
{
  // open a file using the OS
}

enum Fruit
{
  FRUIT_APPLE = 21,
  FRUIT_BANANA = 42
};

Open(MODE_READ | MODE_WRITE); // works now
Open(FRUIT_APPLE | MODE_WRITE); // whoopsy daisy!

Because enum values in C++ get automatically promoted into integers, there’s no type safety anymore – combining fruits and modes is clearly not going to work, but it compiles flawlessly.

Improved enums

The first thing we are going to tackle is the lack of type-safety. What we need is some construct which allows setting different flags easily, and doesn’t automatically get promoted to integer types. A simple class will do:

template <class T>
class Flags
{
public:
  inline Flags(void)
    : m_flags(0)
  {
  }

  inline explicit Flags(T flag)
    : m_flags(flag)
  {
  }

  inline void Set(T flag)
  {
    m_flags |= flag;
  }

  inline void Remove(T flag)
  {
    m_flags &= ~flag;
  }

  inline void Clear(void)
  {
    m_flags = 0;
  }

  inline bool IsSet(T flag) const
  {
    return ((m_flags & flag) != 0);
  }

  inline Flags operator|(Flags other) const
  {
    return Flags(m_flags | other.m_flags);
  }

  inline Flags& operator|=(Flags other)
  {
    m_flags |= other.m_flags;
    return *this;
  }

private:
  inline explicit Flags(uint32_t flags)
    : m_flags(flags)
  {
  }

  uint32_t m_flags;
};

The Flags<> class template now forbids combining values of different enums at compile-time, and we can combine Flags<> using bitwise-OR because of the overloaded operators:

typedef Flags<Mode> ModeFlags;

void Open(ModeFlags mode)
{
  // open a file using the OS
}

enum Fruit
{
  FRUIT_APPLE = 21,
  FRUIT_BANANA = 42
};

Open(ModeFlags(MODE_READ) | ModeFlags(MODE_WRITE));
Open(ModeFlags(FRUIT_APPLE) | ModeFlags(MODE_WRITE)); // doesn't compile

Even though type-safety is now established, there are still some things we can improve.

Improved debuggability

When looking at flags in the debugger, all we see is an integer value:

As soon as several distinct values for flags exist, you have to use your trusted calculator in order to see which flags actually were set.

One could certainly write a Visual Studio plug-in which shows the all the different bits in the debugger, but there’s a much simpler solution – use an appropriately built helper-structure inside an anonymous union inside the Flags<> class template:

enum Mode
{
  MODE_READ = 1 << 0,
  MODE_WRITE = 1 << 1,
  MODE_APPEND = 1 << 2,
  MODE_RANDOM_ACCESS = 1 << 3
};

struct Bits
{
  uint32_t MODE_READ : 1;
  uint32_t MODE_WRITE : 1;
  uint32_t MODE_APPEND : 1;
  uint32_t MODE_RANDOM_ACCESS : 1;
};

template <class T>
class Flags
{
  // ...

private:
  union
   {
    uint32_t m_flags;
    Bits m_bits;
  };
};

Note that each of the members in the Bits struct uses exactly one bit, matching the corresponding value of the enum, and that the Flags<> template still has the same size because of the union. The output in the debugger now looks like this:

Much clearer now.

Now that we need two classes for each bunch of flag-values (the original enum as well as the helper structure), the Flags<> template needs to be adjusted. Instead of taking two different template arguments, we still only require one argument, but make use of nested classes (later we will see why):

struct Mode
{
  enum Enum
  {
    MODE_READ = 1 << 0,
    MODE_WRITE = 1 << 1,
    MODE_APPEND = 1 << 2,
    MODE_RANDOM_ACCESS = 1 << 3
  };

  struct Bits
  {
    uint32_t MODE_READ : 1;
    uint32_t MODE_WRITE : 1;
    uint32_t MODE_APPEND : 1;
    uint32_t MODE_RANDOM_ACCESS : 1;
  };
};

template <class T>
class Flags
{
  typedef typename T::Enum Enum;
  typedef typename T::Bits Bits;

  // implementation...

private:
  union
  {
    uint32_t m_flags;
    Bits m_bits;
  };
};

Because of the nested class structure, we can further improve our flags. Often, it is desireable to turn a flags’ value into a human-readable string for logging. Such a function can now simply be added to the main class which serves as the template argument for the Flags<> template:

struct Mode
{
  enum Enum
  {
    MODE_READ = 1 << 0,
    MODE_WRITE = 1 << 1,
    MODE_APPEND = 1 << 2,
    MODE_RANDOM_ACCESS = 1 << 3
  };

  struct Bits
  {
    uint32_t MODE_READ : 1;
    uint32_t MODE_WRITE : 1;
    uint32_t MODE_APPEND : 1;
    uint32_t MODE_RANDOM_ACCESS : 1;
  };

  static const char* ToString(size_t value)
  {
    switch (value)
    {
      case MODE_READ:
        return "MODE_READ";
      case MODE_WRITE:
        return "MODE_WRITE";

      // ...

      default:
        ME_NO_SWITCH_DEFAULT;
    }
  }
};

Making use of this helper function, we can now add a ToString() function to our Flags<> class template:

template <class T>
class Flags
{
public:
  typedef char Description[512];

  const char* ToString(Description& description) const
  {
    int offset = 0;
    for (size_t i=0; i < T::Count; ++i)
    {
      if ((m_flags & (1u << i)) != 0)
      {
        offset += _snprintf_s(description + offset, sizeof(description) - offset, _TRUNCATE, "%s, ", T::ToString(1u << i));
      }
    }
    // remove the trailing comma, if any
    if (offset > 1)
      description[offset-2] = 0;

    return description;
  }
};

Flags can now easily be turned into human-readable strings by using the ToString() method.

Putting it all together

Of course, maintaining two classes and a helper function whenever adding a new value is error-prone and quite a bit of work, hence we need something which get’s the job done automatically. Again, Molecule’s small preprocessor library proves to be helpful:

#define ME_DECLARE_FLAGS_ENUM(name, n)                    name = (1u << n),
#define ME_DECLARE_FLAGS_BITS(name, n)                    uint32_t name : 1;
#define ME_DECLARE_FLAGS_TO_STRING(name, n)               case name: return ME_PP_STRINGIZE(name);

#define ME_DECLARE_FLAGS(name, ...)    \
struct name    \
{    \
  static const size_t Count = ME_PP_NUM_ARGS(__VA_ARGS__);    \
  static_assert(Count <= 32, "Too many flags used in ME_DECLARE_FLAGS. A maximum number of 32 flags is allowed.");   \
  enum Enum    \
  {    \
    ME_PP_EXPAND_ARGS ME_PP_PASS_ARGS(ME_DECLARE_FLAGS_ENUM, __VA_ARGS__)    \
  };    \
  struct Bits    \
  {    \
    ME_PP_EXPAND_ARGS ME_PP_PASS_ARGS(ME_DECLARE_FLAGS_BITS, __VA_ARGS__)    \
  };    \
  static const char* ToString(size_t value) \
  { \
    switch (value) \
    { \
      ME_PP_EXPAND_ARGS ME_PP_PASS_ARGS(ME_DECLARE_FLAGS_TO_STRING, __VA_ARGS__)    \
      default: \
        ME_NO_SWITCH_DEFAULT; \
    } \
  } \
};    \
inline Flags operator|(name::Enum lhs, name::Enum rhs)    \
{    \
  return (Flags(lhs) | Flags(rhs));    \
}

As you can see, the macro automatically builds the enum class, the helper-class containing the different one-bit-sized members, as well as the ToString() function. Furthermore, it contains an operator| which allows users to use the Flags<> class exactly the same way as enums, without having to explicitly use the Flags<> constructor.

Usage is simple. Just define the possible values using a single line of code:

ME_DECLARE_FLAGS(PlayerState, Dazed, Stunned, Killed, Floating);

The above declares a struct PlayerState which can be used as template argument to the Flags<> template:

typedef Flags<PlayerState> PlayerFlags;

Using the PlayerFlags class is simple and straightforward:

void Do(PlayerFlags flags)
{
  PlayerFlags::Description desc = {};
  ME_LOG0("Flags", "flags: %s", flags.ToString(desc));
}

PlayerFlags state;

// set and remove some flags - type-safe!
state.Set(PlayerState::Dazed);
state.Set(PlayerState::Floating);
state.Remove(PlayerState::Dazed);
state.Set(PlayerState::Killed);
ME_LOG0("Flags", "is any set: %d", state.IsAnySet());
ME_LOG0("Flags", "are all set: %d", state.AreAllSet());
ME_LOG0("Flags", "is Dazed: %d", state.IsSet(PlayerState::Dazed));
ME_LOG0("Flags", "is Floating: %d", state.IsSet(PlayerState::Floating));
ME_LOG0("Flags", "is Killed: %d", state.IsSet(PlayerState::Killed));

PlayerFlags::Description desc = {};
ME_LOG0("Flags", "flags have the following state: %s", state.ToString(desc));

state.Set(PlayerState::Dazed);
state.Set(PlayerState::Stunned);

// explicit constructors
Do(PlayerFlags(PlayerState::Dazed) | PlayerFlags(PlayerState::Killed));

// same as enum, works because of operator|
Do(PlayerState::Dazed | PlayerState::Killed);

The above will print the following:

[Flags] (INFO) is any set: 1
[Flags] (INFO) are all set: 0
[Flags] (INFO) is Dazed: 0
[Flags] (INFO) is Floating: 1
[Flags] (INFO) is Killed: 1
[Flags] (INFO) flags have the following state: Killed, Floating
Advertisements

19 thoughts on “Flags on steroids

    • Nice, thanks for the link!
      Lee’s implementation also covers how to make sure that the flags each occupy the least amount of space – something I didn’t try to tackle, because I prefer a good old int for that purpose.

  1. Hello, my first post here, I follow your blog and everything you write with a great pleasure. I just have one question concerning your solution for flags.
    I developed something similar based on your approach and lee one but I have a problem with the preprocessor under GCC (mingw version). Everything works fine under Visual Studio 2010 but with mingw I get this line not fully processed :

    MA_PP_EXPAND_ARGS MA_PP_PASS_ARGS(MA_DECLARE_FLAGS_ENUM, __VA_ARGS__)

    I get this in the preprocessor file generated by g++ :

    MA_PP_EXPAND_ARGS (MA_DECLARE_FLAGS_ENUM, MODE_READ, MODE_WRITE, MODE_READ_WRITE)

    as if the compiler stopped processing the macros at this point. if I write this exact line in the source code, it process one step further and give this :

    MA_PP_EXPAND_ARGS_3 (MA_DECLARE_FLAGS_ENUM, MODE_READ, MODE_WRITE, MODE_READ_WRITE)

    and of course if I copy this line in the source this time it works. So it seem that it stop processing two level too early.

    I tested with both g++ 4.6.1 and 4.6.2. Do you have an idea why it does that ?

      • I checked what is the reason of the macros not expanding after being created and I found this link explaining the behavior of GNU C PreProcessor (cpp) used in GCC : http://gcc.gnu.org/onlinedocs/cppinternals/Macro-Expansion.html

        It’s exactly the problem that we have with your macros (that I found full implementation in an article on altdevblogaday). In the exemple above, there is a space in front of the parenthesis leading GCC to ignore the macros. I tried to correct it on my own by modifying your macros without success before contacting you again. Do you have an idea how to fix this issue ?

  2. Leaving out the MA_PP_PASS_ARGS macro should do the trick, e.g. try changing the line to the following: MA_PP_EXPAND_ARGS(MA_DECLARE_FLAGS_ENUM, __VA_ARGS__)

    The other occurrences of MA_PP_PASS_ARGS need to be fixed as well.

  3. Just a newb trying to get back into C++ after years of neglect, here. I was never very well educated in macros or templates, but I’m doing my best to digest the material here. I mostly just wanted to point out a typo on line 53 of the complete Flags implementation:

    53 for (size_t i=0; i {

    Hope to see more blog entries in the near future, thanks!

  4. Hi there,

    A couple of questions regarding this. Firstly, why the extraneous static_assert in the ME_DECLARE_FLAGS macro?

    Secondly, it occurred to me that I’m not certain how the values of the enum are getting assigned in this implementation. The expand args is in the format op(a0), op(a1), etc, but the ME_DECLARE_FLAGS_ENUM macro has two parameters. I can’t see how the second parameter is being increased with each value, but I could be missing something entirely.

    Hope you can help, thanks!

    • Hi Ray,

      Regarding your first question: Good point. If you had more than 32 flags, the size of the bits struct would be bigger than 32 bits, and hence the flags would not fit into the uint32_t m_flags anymore. I’m not entirely sure whether all compilers would emit a warning in such a situation, thus I added the static_assert.

      To answer your second question, the ME_PP_EXPAND_ARGS automatically takes care of that. It takes arguments a0, a1, …, aN and expands them into op(a0, 0), op(a1, 1), and so on. I don’t know the implementation by heart, but I’ll let you know once I’m in the office. It’s nothing special, really.

      • As an update to my previous answer, here’s some details about the ME_PP_EXPAND_ARGS macro:

        There’s several versions of the macro, like the following:
        #define ME_PP_EXPAND_ARGS_2(op, a1, a2) op(a1, 0) op(a2, 1)
        #define ME_PP_EXPAND_ARGS_3(op, a1, a2, a3) op(a1, 0) op(a2, 1) op(a3, 2)
        and so on.

        Now in order to not having to specify different macros based on the number of arguments, the ME_PP_EXPAND_ARGS(op, …) macro counts the number of provided arguments, and “calls” the correct macro, passing all arguments. That one *is* tricky, and I can share details if there’s interest.

  5. Well I certainly feel like an idiot now! Thanks very much for your detailed reply, definitely cleared things up with the macro 🙂

    Just to clarify regarding the static_assert, I meant that the code above displays:

    static_assert(Count enum Enum \

    rather than static_assert(Count == sizeof(uint32_t)) which is probably what you intended?

    Thanks again!

    • Whoops, just looked at the code – the static_assert doesn’t make any sense at all, thanks for spotting! Either WordPress ate some of the brackets, or I just messed up while writing.

      The assert should be:
      static_assert(Count <= sizeof(uint32_t)*8)

      This ensures that all the bits from the bitfiels fit into an uint32_t.

  6. Hi Stefan,
    Thanks for this great article! Very helpful as always 🙂

    I would just like to ask you how ME_NO_SWITCH_DEFAULT is defined; it’s been bugging me for a while… Is it a compiler hint or an empty macro ?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s