10

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • So it's really about taking a value of some integral type and seeing whether it's within the range of some other integral type. Not fundamentally about enums. Perhaps https://codereview.stackexchange.com/q/5515/2503? – Lightness Races in Orbit Nov 07 '18 at 13:41
  • Unless by "valid value" you mean "enumerated value"? (Because all `int`s are valid for the `my_enum_class` shown above) – Lightness Races in Orbit Nov 07 '18 at 13:42
  • This is nice question. In practice, I would've cheated using X macros somehow. (With this, I could even make a `switch()` where every enum value is a case to check and with this make a check at program start which is as near as good as a compile time check.) But, may be, it's a chance to learn to do this better... – Scheff's Cat Nov 07 '18 at 13:43
  • 2
    related/dupe: https://stackoverflow.com/questions/4969233/how-to-check-if-enum-value-is-valid – NathanOliver Nov 07 '18 at 13:45
  • 2
    Your biggest problem you are going to have is any value that is within the values provided in the enum will be valid, even if it doesn't actually map to an enum value. This needs reflection which we don't have so you have to build the map yourself. – NathanOliver Nov 07 '18 at 13:47
  • @LightnessRacesinOrbit: I did mean "enumerated value", see edit. "valid" is a word with many interpretations. – einpoklum Nov 07 '18 at 13:48
  • @NathanOliver: See edit – einpoklum Nov 07 '18 at 13:48
  • Problem is, IIRC, that *any* `int` is a valid enum value. The enumerators are just the names of *specific* ones. – StoryTeller - Unslander Monica Nov 07 '18 at 13:49
  • @StoryTeller: See edit... – einpoklum Nov 07 '18 at 13:50
  • @einpoklum See my second comment. – NathanOliver Nov 07 '18 at 13:50
  • 1
    @einpoklum - Your edit doesn't affect my comment. Just `s/int/I/` – StoryTeller - Unslander Monica Nov 07 '18 at 13:50
  • 1
    @NathanOliver: So your answer is "Not at the moment, but possibly in the future with static reflection that's under committee discussion"? – einpoklum Nov 07 '18 at 13:51
  • @einpoklum Yes. There might be a library you could use to get this right now but it isn't provided by the standard. All the standard talks about is if the value is valid, not if it actually maps to an enumeration. It doesn't provided a way to check if the "valid value" actually maps to an enumeration. – NathanOliver Nov 07 '18 at 13:52

3 Answers3

9

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
7

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.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I actually wanted to use this to enhance [this](https://stackoverflow.com/questions/38914655/idiom-for-simulating-run-time-numeric-template-parameters) numeric range visitation mechanism so as to support non-contiguous enums better. – einpoklum Nov 07 '18 at 14:03
2

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))
einpoklum
  • 118,144
  • 57
  • 340
  • 684
xtofl
  • 40,723
  • 12
  • 105
  • 192