How should I check whether an underlying-type value is an enumerated value?

Viewed 833

Let I be some integral type. Now suppose I have a enum class my_enum_class : I, with values which may not be consecutive. And now I get some I value. How do I check whether it's a value enumerated in my_enum_class?

An answer to a similar question (for the C language) makes the assumption that values are contiguous, and that one can add a "dummy" upper-bound value, and check the range between 0 and that value; that's not relevant in my case. Is there another way to do it?

3 Answers

There is currently no way to do this.

There are reflection proposals that may make it into and/or that let you iterate (at compile, and hence run, time) over the enumerated values in an enum. Using that the check would be relatively easy.

Sometimes people do manual enum reflection, often using macros.

There is no built-in way to do this. All Is are "valid" values of my_enum_class, so you can't do anything with the underlying type. As for validating Is against the list of enumerators, without reflection there is simply no way to do it.

Depending on the context, I tend to either build a static std::unordered_set (and do lookups into that), or have a function listing all my enumerators in a switch (and returning false iff the input matches none of them), or just not bother, instead documenting somewhere that passing an unenumerated my_enum_class value to my functions shall be deemed impish trickery and have unspecified behaviour.

Ultimately this all stems from the fact that enums are supposed to list "common conveniently named values" within a wider range of totally valid states, rather than a type comprised only of a fully constrained set of constants. We pretty much all abuse enums.

Though the standard doesn't yet allow you to do introspection, there is a small workaround you could use, that can possibly be improved with ADL. Courtesy to this older answer.

namespace sparse {
  template<typename E>
  constexpr bool in_(std::underlying_type_t<E> i) { return false; }

  template<typename E, E value, E...values>
  constexpr bool in_(std::underlying_type_t<E> e) {
    return static_cast<E>(e) == value || in_<E, values...>(e);
  }
}

To be used like this:

enum class my_enum: int { a=3, b=4 };

template<>
constexpr auto sparse::in<my_enum> =
     in_<my_enum, my_enum::a, my_enum::b>;

static_assert(sparse::in<my_enum>(3));
static_assert(sparse::in<my_enum>(4));
static_assert(!sparse::in<my_enum>(5))
Related