Tag Archives: macro

Syncing Enums and Arrays

puzzleHere is a basic technique that may not be familiar to all…

Often, the need arises to define a symbolic enumeration, along with a “companion” array of complementary data such as string names.  With default compiler-assigned values, the enumeration serves as a convenient index into the companion array, as in the following example:

// Enumeration and companion array, often 
// accompanied by a comment like this:
// WARNING: Must keep ColorEnum and ColorNames in sync!
enum ColorEnum
{
	Red,
	Green,
	Blue
};

static const char * ColorNames[] = 
{ 
	"Red",
	"Green",
	"Blue"
};

void PrintColor(ColorEnum c)
{
	cout << ColorNames[c];
}

The risk of this technique is in maintaining coordination between symbols and slots - both the sequence and the count.  Beyond that, there is violation of the DRY principle ("don't repeat yourself"), creating more opportunities for things to get out of sync.

The solution is to use a pair of macros.  The first, which we'll call sequence, defines an abstract sequence of elements.  The second, which we'll call select, defines the attributes of each element.  Often, the function of the select macro is simply to select one or more entities from the abstract definition in sequence.  But the capability is general and can be used to define any sequence, including enums, static C-style arrays, static const members, parse tables, etc.

Following is a code sample demonstrating the basic idea.

// Define abstract sequence of elements, each of which 
// programmatically "selects" appropriate attributes
#define sequence \
	select( Red, 0xFF0000 ) \
	select( Green, 0x00FF00 ) \
	select( Blue, 0x0000FF )

// Generate symbolic enum: Red, Green, Blue
#undef select
#define select(symbol, value, ...) symbol,
enum ColorEnum { sequence };

// Generate color name sequence: "Red", "Green", "Blue"
// Indexed by ColorEnum above
#undef select
#define select(symbol, value, ...) #symbol, 
static const char * ColorNames[] = { sequence };

// Generate color value sequence: 0xFF0000, 0x00FF00, ...
// Indexed by ColorEnum above
#undef select
#define select(symbol, value, ...) value,
static int ColorValues[] = { sequence };

// Generate color struct sequence: {"Red", 0xFF0000}, {"Green", 0x00FF00}, ...
// Indexed by ColorEnum above
// Note the definition of select as a variadic macro, 
// to conveniently pick up all remaining element values.
#undef select
#define select(symbol, ...) { #symbol, __VA_ARGS__ }, 
static struct Color
{
	const char * symbol;
	int value;
}
Colors[] = { sequence };

// Define public static color symbols with numeric values
#undef select
#define select(symbol, value, ...) static const int symbol = value;
struct Colors { sequence };

For a deeper dive into code-gen with the preprocessor, I recommend reading Appendix A An Introduction to Preprocessor Metaprogramming from the excellent "C++ Template Metaprogramming" by David Abrahams and Aleksey Gurtovoy.