Boost preprocessor + enums

Update 2015

I’ve received a very friendly e-mail this weekend from Anton Bachin, the author of the better enums library. Some time has passed since I originally wrote the post below and C++ has improved quite a lot in the meantime, his library seems a much nicer solution! So feel free to read along below but if you have a need for this functionality, definitely consider using his library instead. Thanks Anton for bringing it to my attention!

Original post

A recurring problem in C++ is printing out enum values. Of course you can just do ‘std::cout << enumval << std::endl;’ but that will only print the numeric value. For logging purposes it would be nice to print out the textual representation of the enum value.

Usually what people do is add some kind of utility ‘toString’ method and add a load of ‘if’/’case’ statements that will match all enum values and return a string. I found this to be error-prone, because you will need to update both the enum and this utility function at the same time to keep consistent. So I thought about this for a while and decided perhaps the Boost preprocessor library could come to the rescue!

Check out this sample code:

#include <string>
#include <iostream>
#include <boost/preprocessor.hpp>

// Used in toString() method
#define ENUM_TO_STR(unused,data,elem) \
if (parm == elem) return BOOST_PP_STRINGIZE(elem);

class EnumTest
{
public:
   // Need to undef if because you might have
   // multiple enum definitions in a file
#undef SEQ
#define SEQ (_INVALID_)(VALUE1)(VALUE2)(VALUE3)(_MAX_)

   enum SampleEnum
   {
      BOOST_PP_SEQ_ENUM(SEQ)
   };

   static const std::string toString(const SampleEnum parm)
   {
      BOOST_PP_SEQ_FOR_EACH(ENUM_TO_STR,~,SEQ)
      return "_INVALID_";
   }
};

int main()
{
   EnumTest::SampleEnum t1 = EnumTest::VALUE1;
   std::cout << EnumTest::toString(t1) << std::endl;
}

Some comments:

  1. The enum is generated by the BOOST_PP_SEQ_ENUM macro, which relies on a preprocessor definition called ‘SEQ’ to contain a list of values. These values should be encapsulated in () braces.
  2. The static ‘toString’ method uses the BOOST_PP_SEQ_FOR_EACH macro. This macro repeats a specified statement (in this case the ENUM_TO_STR macro) for each element in SEQ. The ‘~’ will be passed as additional data to the ENUM_TO_STR macro and put in the ‘unused’ parameter. I don’t use this functionality here but it could be useful in other places :)

If you want to see the generated code from the preprocessor here is the result (the ‘if’ statements are a bit messy, I could insert a newline there perhaps):

enum SampleEnum
{
   _INVALID_, VALUE1, VALUE2, VALUE3, _MAX_
};

static const std::string toString(const SampleEnum parm)
{
   if (parm == _INVALID_) return "_INVALID_"; if (parm == VALUE1) return "VALUE1"; if (parm == VALUE2) return "VALUE2"; if (parm == VALUE3) return "VALUE3"; if (parm == _MAX_) return "_MAX_";
   return "_INVALID_";
}

It’s not extremely pretty but it works :)