Need help with wise_enum?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

243 Stars 25 Forks Boost Software License 1.0 78 Commits 12 Opened issues


A reflective enum implementation for C++

Services available


Need anything else?

Contributors list

# 77,485
59 commits
# 569,209
7 commits
# 283,438
3 commits
# 62,616
2 commits
# 324,190
1 commit
# 28,165
1 commit
# 639,292
1 commit
# 52,956
1 commit
# 349,691
1 commit

wiseenum [Build Status]( Build status

Because reflection makes you wise, not smart

is a standalone smart enum library for C++11/14/17. It supports all of the standard functionality that you would expect from a smart enum class in C++: - Tells you the number of enumerators - Lets you iterate over all enum values - Converts string to enum, and enum to string - Does everything in an idiomatic C++ way (friendly to generic programming, compile time programming, etc)


Let's look at a bit of code. You can declare an enum like this:

// Equivalent to enum Color {GREEN = 2, RED};

You can also declare an enum class instead of an enum, specify the storage explicitly, declare an enum nested inside a class, or even adapt an already declared enum:

// Equivalent to enum class MoreColor : int64_t {BLUE, BLACK = 1};
WISE_ENUM_CLASS((MoreColor, int64_t), BLUE, (BLACK, 1))

// Inside a class, must use a different macro, but still works struct Bar { WISE_ENUM_MEMBER(Foo, BUZ) };

// Adapt an existing enum you don't control so it works with generic code namespace another_lib { enum class SomebodyElse { FIRST, SECOND }; } WISE_ENUM_ADAPT(another_lib::SomebodyElse, FIRST, SECOND)

You can ask the enum how many enumerators it has:

static_assert(wise_enum::size == 2, "");

Iterate over the enumerators:

std::cerr << "Enum values and names:\n";
for (auto e : wise_enum::range) {
  std::cerr << static_cast(e.value) << " " << << "\n";

Convert between strings and enums:

// Convert any enum to a string
std::cerr << wise_enum::to_string(Color::RED) << "\n";

// Convert any string to an optional auto x1 = wise_enum::from_string("GREEN"); auto x2 = wise_enum::from_string("Greeeeeeen");

assert(x1.value() == Color::GREEN); assert(!x2);

Check whether something is a wise enum at compile time:

static_assert(wise_enum::is_wise_enum_v, "");
static_assert(!wise_enum::is_wise_enum_v, "");
enum flub { blub, glub };
static_assert(!wise_enum::is_wise_enum_v, "");


It has a few notable design choices.

First, when you use one of the macros to declare your enum, what gets declared (among other things) is exactly the vanilla enum (or enum class) that you would expect. Not an enum like class, or anything like that. That means that

s can be used exactly like regular enums in non-reflective contexts, because they are regular enums. They can be used as non-type template parameters, and they can be used in switch case statements, unlike any user defined type. This also means that upgrading a regular enum already widely used (non-reflectively) in your codebase to a wise enum is never a breaking change. No strange behavior, or edge cases, when used with other third party libraries (e.g. serialization), or standard library type traits.

Second, all the functionality in defining enums is preserved. You can define

enum class
es, set storage explicitly or let it be implicit, define the value for an enumeration, or allow it to be determined implicitly. You can also define enums nested in classes, which isn't supported in some smart enum libraries.

Third, it's quite compile-time programming friendly. Everything is

, and a type trait is provided. This makes it easy to handle wise enums in a specific way in generic code. For example, if you have a logger, it can't intelligently log a vanilla enum without more information, but it can log a wise enum, so you can use the type trait to handle them differently (with no performance cost of course).

Fourth, it's careful with regards to performance and generated assembly. It makes zero heap allocations and does zero dynamic initialization, and does not use exceptions. The enum -> string is an optimal switch-case. String -> enum is currently a linear search; this may be changed in the future (most alternatives are not trivial to implement without doing heap allocations or dynamic initialization).

Quick Comparison of Alternatives

The best known alternative is probably Better Enums. The biggest issue with Better Enums is simply that its macros don't actually create enums or enum classes, but enum like classes. This carries all of the disadvantages discussed in the previous section, and for me was just a deal breaker. There are also more minor issues like not being able to define a nested enum, having a lower default enumeration limit. Conversely, I'm not aware of any advantages, except one: it does support C++03, which wise enum never will, so it's a good choice for older codebases.

A recent interesting alternative is Meta Enum. This does declare actual enum/enum classes. As benefits, it doesn't have any limit on the number of enumerations by design, and it doesn't require different macros for declaring enums nested inside classes. As far as I can tell though, the approach means that it can't support switch case generation (e.g. for to_string), nor can it support 11. It currently only seems to support 17 but 14 support may be possible.

As far as I saw, neither library has something like the adapt macro, though I think either one could add it pretty easily. There are other implementations, but most of the ones I've seen are very clearly very short projects, lacking support for basic features (e.g. controlling enum values) and documentation.

Overall, I feel like wise enum is the best choice for an enum library for a typical, modern C++ codebase.

If any of this information is incorrect, please let me know and I'll make correcting it the highest priority.

Version differences

Wise enum tries to target each language version idiomatically. In 11, template variables, which are the recommended interface in 14/17 are not available so using the typical class template static interface instead is necessary. Many functions lose

in 11. The difference between 14 and 17 is mostly in the types used, discussed in the next section.

Types and Customizations

There are two types that you can customize in wise_enum, by defining macros: the optional type, and the string type.

| Type | 11/14 default | 17 default | customize macro | type alias | | ------------- | ----------------- | ---------- | --------------- | --- | | optional |

| | string |
const char *

If you only support 17, the defaults should be fine. If you're on 11/14, the defaults are fine as well, but if you want to be forward compatible I'd consider rounding up a stringview implementation somewhere and using that. Otherwise, since

const char*
and `stringview` don't have the same interface, you may have breakages when upgrading. Finally, if you're supporting both 11/14 and 17 I'd definitely define both macros so the same type is used in your builds.

You can define the macro either in your build system, or by having a stub header that defines them and then includes

, and only including via the stub.

You can also customize the use of exceptions. If you use the CMake option

or otherwise define the macro
, then wiseenum should be fully compatible with
. The API never directly throws exceptions anyhow; the only change in behavior is that the provided
(and `compact
) will abort rather than throw on
value()` calls if the optional is empty, similar to the behavior of most (all?) standard library optional implementations when exceptions are disabled.

Extra Features

Over time I'd like to leverage some of the capabilities of wise enum to do other useful enum related things.

Compact optional

I have a compact optional implementation included now in wise enum. The key point is that it uses compile time reflection to statically verified that the sentinel value used to indicate the absence of an enum, is not a value used for any of the enumerators. If you add an enumerator to an enum used in a compact optional, and the value of the enum is the sentinel, you get a compilation error.

Switch case "lifts"

One problem where C++ gives you little recourse is when you have a runtime value that you want to lift into a compile time value. Consider the following:

class MyDerived : MyInterface {...};

unique_ptr make(MyEnum e);

It's hard to write

without boilerplate in the general case. You need to manually switch case over all enumerator values, and in each case put the compile time constant into the template. Wise enum will shortly (exact interface being worked out) provide a facility that takes an enum and a lambda, does the switch case over all values internally, and calls the lambda making the enum available as a compile time constant.

Coming soon!

Enum Sets

Planned for the future.


There are some known limitations:

  • If there are enumerators with the same value, then
    will not work. You can declare the enum and use all the other API. This is both because it doesn't jive at all with the implementation, and even conceptually it's not clear how you would handle a conversion to string since multiple strings would be associated with the same value.
  • By default, you are limited to 256 enumerations. If you need more, simply run the
    script to create a file with as many as you need, and replace
    with that file. The default limit may be raised or lowered based on feedback. An alternative solution here would be to create headers in advance that raise this number, but leave the onus on the user to include them (so users who don't need a large number aren't slowed down)


Why didn't I use

I started with it, but the limit on sequences was very disappointing (64) and there didn't seem to be any easy way to change it. So then I started down the codegen route, and once there, I wasn't using very much. I know there are always people who prefer to avoid the dependency, so I decided to drop it.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.