A C++ Printable Enum

Ever wanted to have enum entries represented or accessible as strings?  This is a tool for the job.

A regrettable limitation of C++ enums, is the lack of a printable or string form of the enum entries. Since reflection is not an option (e.g. Java or C#), and manual creation and maintenance of a matching string array is intolerable, the only remaining option is a preprocessor macro. Once such example is a two-pass printable enum presented on codeplex.  Below, however, is a one-pass approach I created which I think is more flexable and concise.  The macro depends on the boost preprocessor library.  The first part is the macro itself:

/*
header description: this is a preprocessor based "printable enum";
Enum entries are created with a corresponding string array, that
can be indexed by the enum entries.

Author: Brent Arias
License: permission is given to use or modify freely, commercially
or otherwise.  Please retaint the author name in source code (Brent Arias).
*/

#ifndef   PRINTABLE_ENUM
#define PRINTABLE_ENUM

#include <boost/preprocessor/seq/seq.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/logical/bitor.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/tuple/eat.hpp>

/***************************************************************
BOOST_PP_EXPR_IIF requires precompiler definition arguments
(viz. PRINTABLE_ENUM_ENUMS, PRINTABLE_ENUM_STRINGS,
PRINTABLE_ENUM_DEBUG) to be either 0 or 1.
This will not build otherwise!!!!
****************************************************************/

/*Usually the ENUM macro should actually expand into an enum,
"define ENUM 1" sets that default*/
//By default, turn enums on
#ifndef PRINTABLE_ENUM_ENUMS
#define PRINTABLE_ENUM_ENUMS 1
#endif
//By default, turn printable strings off
#ifndef PRINTABLE_ENUM_STRINGS
#define PRINTABLE_ENUM_STRINGS 0
#endif
#ifndef PRINTABLE_ENUM_DEBUG
//By default, turn debuggin "compliment" condition off
#define PRINTABLE_ENUM_DEBUG 0
#endif

/*Used internally by the ENUM macro*/
#ifdef ENUM_STR
#undef ENUM_STR
#endif
#define ENUM_STR(r, data, elem) BOOST_PP_STRINGIZE(elem)

/*Make sure there is no space after the '\' line wrap - or you'll
spend hours pursuing "too few arguments" or "too many arguments"
compiler errors.*/

#define ENUM(name, start, entries)                       \
	BOOST_PP_IIF(PRINTABLE_ENUM_ENUMS,                       \
	BOOST_PP_SEQ_ENUM, BOOST_PP_TUPLE_EAT(1))             \
	(                                                     \
	(typedef enum{ BOOST_PP_SEQ_HEAD(entries) = start) \
	BOOST_PP_SEQ_TAIL(entries)                         \
	(LAST_##name } name;)                              \
	)                                                     \
	BOOST_PP_IIF(BOOST_PP_BITOR(PRINTABLE_ENUM_DEBUG,PRINTABLE_ENUM_STRINGS), \
	BOOST_PP_SEQ_ENUM, BOOST_PP_TUPLE_EAT(1))             \
	(                                                     \
	(static const char* name##_Str[]={ BOOST_PP_STRINGIZE(BOOST_PP_SEQ_HEAD(entries)) ) \
	BOOST_PP_SEQ_TRANSFORM(ENUM_STR, _, BOOST_PP_SEQ_TAIL(entries) ) \
	(};)                                               \
	)

#endif

With the above macro recorded in its own header file, it can be used as often as needed for enum definitions. Take for example a ‘myEnumDefinition’ that is made up of colors, starting at index ’0′. The macro is then used as follows:

#define PRINTABLE_ENUM_STRINGS 1
#include "PrintableEnum.h"
	ENUM(myEnumDefinition, 0,
	(YELLOW)           /*enums delimited with parens instead of commas*/
	(RED)
	(GREEN)
	(BLUE)
	(MAGENTA)
	)

The above ENUM macro expansion:

  1. creates the “myEnumDefinition” typedef
  2. starts the enumeration at zero
  3. creates the actual enum values (e.g. GREEN, BLUE, etc)
  4. creates the string array “myEnumDefinition_Str[]‘
  5. fills the string array with values such as “GREEN”, “BLUE”, etc
  6. creates a “tail” enum value called “LAST_myEnumDefinition”

Just remember that the string array for the enum bears the same name as the enum, but appended with “_Str”. That’s it!

Share

Leave a Reply

To comment, click below to log in.