Smart enums in C++ or: "what is this madness?"

Needless to say, C++ is a glorious language. Sadly, the attention it gets on the blog is far from representative of my appreciation towards it. Despite it being my main language at work, I only tend to talk about Libgdx side projects around here. Well, enough is enough!

Truth be told, over the years, standard after standard, C++ has become a behemoth of a language. As Scott Meyers likes to say, it’s actually a set of languages.

One of the beauties of C++ is that I can always learn something new about it, which almost makes up for its ever growing syntax complexity.

Let’s talk macros

Let’s talk about macros. Everyone knows about the preprocessor and the basic usage of macros, right?

Actually, let’s talk about macros that take other macros as arguments.

Sorry what?

The problem

Let us imagine we define the following perfectly standard enum to represent kinds of animals in a farm.

Our application can request a server a list of animals a given farm has. The server sends the animal names as strings, so we need to convert them to enum values. Alright, let’s define a function to do that.

Err… That is not the most elegant piece of code you’ve ever seen, is it? We also might have to convert from an enum value to a string literal in case we want to send a report back to the server.

That was horrendous enough, but just the thought of adding new species to the catalogue makes me shiver badly. Why not simply use strings to represent animal species? Enums are nice because they heavily reduce the domain of values a variable can have and prevent us from making silly typos everywhere.

There must be a better solution.

Higher order macros

To make things nicer, we’d need some sort of compile time mechanism that traverses the list of enum values generating the time consuming boilerplate code for us.

The preprocessor!

Take a look at this way of defining our list of animals.

_ANIMAL_LIST_ is a higher order macro that takes a macro m as a parameter. Then it passes each one of our animal species to m.

The previous macro is not really useful by itself. Let’s make things slightly more interesting

SMARTENUM_DEFINE_ENUM takes the enum name and the list of values as parameters and defines an enum for us. It also appends the total count.

When we write

The preprocessor expands it to

Okay, that was a lot of gibber jabber for very little gain.

Hang on a minute, we can go further. Let’s write a small mechanism to convert an enum value to a string literal.

SMARTENUM_DEFINE_NAMES takes an enum type and the list of values and generates an array with the string representations of our list values. Interestingly enough, the values in the array map directly to those of the enum. This allows us to write the third macro, which accesses the array at the right position to return the corresponding string literal for a given enum value.

When we write

The preprocessor expands it to

So

Will result in

Aha!

Getting an enum value from a string literal is slightly trickier but still achievable. We’ve come this far, so it’d be a shame not to finish the job.

The first abomination generates a function that iterates over the previously defined array of names doing string comparisons until it finds the desired enum value. The second one is nothing more than a convenience macro to spare the user from remembering the preprocessor generated function name.

Now if we do

We can simply go

Neat, isn’t it?

For future references

Now, every time we want to define an enum type that we need to convert to and from strings we’ll be lucky to be able to do the following.

VoilĂ !

Admittedly, using macros like this doesn’t make for readable code and sometimes results in debugging complications. However, I’ve been able to debug my code normally in Visual Studio 2012 when using similar macros.

String conversion of enums is just the tip of the iceberg, this approach can be used to generate all sorts of repetitive boilerplate code.

Download it

In case you are interested, SmartEnums.h is available as a GitHub gist.

comments powered by Disqus