Better Enums

Clean compile-time reflective enums for C++11

C++98 support coming soon without the "compile-time" :)

Have you noticed the awkward situation with enums in C++? Built-in enum class is missing basic features, such as string conversion. There are several approaches to address this, but most seem to involve unnatural syntax or code repetition. See some here.

Better Enums gives you rich, reflective enums with the nicest syntax yet seen.1 All you have to do is add one short and simple header file to your project, and you are ready to go.

#include <enum.h>
ENUM(Channel, int, Red, Green, Blue);

This gives you an int-sized enum type with all sorts of reflective capacity, including string conversions, value listing, compile-time operations, static information about the range of declared constants, and (last and probably least) the name "Channel" itself. You can even assign explicit values to constants and alias them like with a normal built-in enum.

The enum is easy to maintain. There is no duplication of value names, no repetition of cumbersome macros, and no generator to run on every build. The library is header-only and has no dependencies, so there aren't any object files to link with. It is also standard C++.

Better Enums is free, available under the BSD license. The author is committed to further development and support, so please contact me, open an issue on GitHub, or ask a question on Stack Overflow.

Installation

Download enum.h and copy it to your project. That's it! Just make sure it's in your include path and you are compiling with gcc or clang in C++11 mode.

msvc support is coming in a near-future version with some enum features disabled. This is because msvc's support for constexpr is lagging. The same patch will probably make it possible to use Better Enums with C++98.

Tutorial

Create a file and put this code in it:

#include <iostream>
#include <enum.h>

ENUM(Channel, int, Red, Green, Blue);

int main()
{
    Channel   my_channel = Channel::Green;
    std::cout
        << "Channel "
        << my_channel.to_string()
        << " has value "
        << my_channel.to_integral()
        << std::endl;

    return 0;
}

Compile and run, and you should see the output Channel Green has value 1. Congratulations! You have compiled your first Better Enum.


Values are assigned to Better Enums just like to regular C++ enums. That means you can change the enum above to something like this:

ENUM(Channel, int, Red, Green = 5, Blue, Last = Blue);

and the result would be just as you'd expect: Red is 0, Green is 5, Blue is 6, and Last is also 6.


There is no need for Last, however. Every Better Enum comes with a built-in value called _last. So, if you have

ENUM(Channel, int, Red, Green, Blue);

Then Channel::_last is Channel::Blue! In fact, Channel also gets _first, _min, _max, _span, and _size. These built-in values have underscores so that you can define your own enums without having to worry about naming problems:

ENUM(Position, int, first, last, other);

You already saw how to convert an enum to a string or an integer. As you might guess, it is also possible to go the other way:

Channel   my_channel = Channel::_from_string("Blue");
Channel   my_channel = Channel::_from_integral(2);

If your code tries to convert an invalid string or integer to a Channel, it will get an std::runtime_error exception.


You can iterate over all the declared values of an enum:

for(Channel channel : Channel::_values)
    std::cout << channel.to_string() << std::endl;

and directly over their names with Channel::_names.


Finally, all of the above can be done at compile time. This means you can do all sorts of parsing and processing at the same time the rest of your code is being compiled, improving runtime and startup performance! See some examples 2 3.

Where to go from here