A nifty little preprocessor trick for C++

We found out something a little surprising about C++ enums a while back.  It turns out that if you have this code:

 

class Foo {

public:

      enum Color {

            Red = 1 << 0,

            Green = 1 << 1,

            Blue = 1 << 2

      };

      void Bar() {

            Color purple = Color::Red | Color::Blue;

      }

};

 

It turns out that this code is illegal for a few reasons.  First of all, it turns out that you can’t refer to elements of a nested enum through the enum name.  Instead you need to refer to them through the outer type’s name.  i.e. instead of Color::Red, you’d say Foo::Red, Foo::Green or Foo::Blue.  Secondly, while enums are implicitly convertible to integers, the reverse is not true.  So applying the binary operator | to two enum values produces an int which then needs to be casted back to the enum type.  So the code would actually have to look like:

 

      void Bar() {

            Color purple = (Color)(Red | Blue);

      }

 

Or, if you’re external to the class it’ll look like

 

      void Bar() {

            Foo::Color purple = (Foo::Color)(Foo::Red | Foo::Blue);

      }

 

Neither of which are particularly nice to use in practice.  Casting means that even operators like |= are unpleasant to use.   And referring to the values through the outer type name is just confusing.  An alternative to solve the latter problem is to just make the enums top level.  i.e. have something like:

 

enum Color {

      Red = 1 << 0,

      Green = 1 << 1,

      Blue = 1 << 2

};

 

That makes things a little nicer now that you can refer to the elements trough the more natural Color::Red.  However, we’re also now polluting the top level namespace with the enum member names.  This means that I now can’t add an enum like:

 

enum StopLightState {

      Red,

      Yellow,

      Green

};

 

because the “Red” value will conflict.   So in our codebase we had these enums defined using ugly prefixes in order to not pollute the namespace (i.e. Color_Red, Color_Green, Color_Blue), and we would also just store those values into DWORDs so that we wouldn’t have to be casting on every operation.

 

Clearly this was pretty suboptimal.  The naming is just redundant we didn’t like that code duplication (not to mention the ugliness of the prefixes), and by using DWORDs we were losing type safety.  So what can we do about this?  Well, thanks to a few C++ tricks it’s easily remediable.  Instead of how I defined the enums as I did above instead try using the following pattern:

 

struct Color {

private:

      Color() {}

public:

      enum _Enum {

            Red = 1 << 0,

            Green = 1 << 1,

            Blue = 1 << 2

      };

};

typedef Color::_Enum ColorEnum

inline ColorEnum operator &(ColorEnum e1, ColorEnum e2) {

      return (ColorEnum)(e1 & e2);

}

inline ColorEnum operator <<(ColorEnum e, int shift) {

      return (ColorEnum)(e1 << shift);

}

//...define the rest of the operators that you’d like

 

Now I can write code just like this:

 

      void Bar() {

            ColorEnum purple = Color::Red | Color::Blue;

      }

 

It’s now typesafe (no more DWORDs), and it allows convenient naming of the enum values without polluting the main namespace.

 

It’s turned out to be so useful that we now have macros to do the work for us.  Specifically:

 

#define DECLARE_ENUM(name) \

      struct name { \

      private: \

      name() {} \

      public: \

      enum _Enum {

#define END_ENUM(name) \

      }; \

      }; \

      typedef name::_Enum name##Enum;

#define FLAGS_ENUM(name) \

      inline name::_Enum operator &(name::_Enum e1, name::_Enum e2) { \

      return (name::_Enum)(e1 & e2); \

      } \

      // … rest of the operators you care about go here.

 

Once you’ve done this you can now just write:

 

DECLARE_ENUM(Color)

      Red = 1 << 0,

      Green = 1 << 1,

      Blue = 1 << 2

END_ENUM(Color)

FLAGS_ENUM(Color)

 

And now all of that boilerplate code will be written for you. 

Useless for you?  Quite possibly :)   But very helpful for me.

 


Edit: I've added in the values that the enum members are initialized to so that they can be used effectively as named flags.  However, it's somewhat annoying to force you to remember to write in (1 << n) for every member.  Anyone have any ideas for how one might get that automatically?