Better Enums

Reflective compile-time enums for C++

Open-source under the BSD license

Version 0.10.1

To install, just add enum.h to your project.

Visit the GitHub repo for issues, feedback, and the latest development.

Download enum.h GitHub

General underlying types

The underlying type of a Better Enum doesn't have to be an integral type. It can be any literal type T, as long as you provide a constexpr two-way mapping between T and an integral type of your choosing. It also works in C++98, though, of course, T doesn't have to be literal and the mapping doesn't have to be constexpr — everything will be done by Better Enums at run time.

Doing this enables the following usage:

// The type. A color triplet.
struct html_color {
    uint8_t     r, g, b;

    constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) :
        r(_r), g(_g), b(_b) { }
};



// The enum.
ENUM(Color, html_color,
     darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954)



// The usage.
Color   c = Color::darksalmon;

std::cout << "Red component: " << c->r << std::endl;

switch (c) {
    case Color::darksalmon:   // ...
    case Color::purplemimosa: // ...
    case Color::slimegreen:   // ...
}

As you can see, you can have an enumerated set of any literal type, and safely use the values in switch, with the compiler checking exhaustiveness. You can also access the type's members using the enum->underlying_member syntax.

You do have to supply the mapping to an integral type, however. One option is:

// The mapping. It just stuffs bits.
template <>
struct ::better_enums::underlying_traits<html_color> {
    using integral_representation = unsigned int;

    constexpr static html_color from_integral(unsigned int i)
        { return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); }

    constexpr static unsigned int to_integral(html_color c)
        { return (unsigned int)c.r << 16 | (unsigned int)c.g << 8 | c.b; }
};

Using constructors in initializers

The declaration above used only numeric initializers. It is possible to use the type's own constructors, provided the type has a constexpr conversion to your chosen integral type:

// The type.
struct html_color {
    uint8_t     r, g, b;

    constexpr html_color(uint8_t _r, uint8_t g, uint8_t b) :
        r(_r), g(_g), b(_b) { }

    // This is new:
    constexpr operator unsigned int() const
        { return (unsigned int)r << 16 | (unsigned int)g << 8 | b; }
};

// The enum.
ENUM(Color, html_color,
     darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954,
     celeste = html_color(0x50, 0xeb, 0xec))

This is not possible at all in C++98, however.

Letting the compiler enumerate your literal type

You don't have to use initializers. For example, as long as your example type file_descriptor knows how to deal with the values, you can have the compiler generate them in sequence:

ENUM(FD, file_descriptor, STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...)

SAMPLE

You can see the code "in action" in the test case. Be aware that it's not very "nice," because it uses conditional compilation to run under multiple compilers. I haven't written a clean sample or documentation yet simply because this feature is in a very early stage of development.

Discussion

This feature is still semi-experimental, though I expect it to remain stable, except perhaps that I will make it possible to infer the type integral_representation.

Any opinions are welcome.