Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to safely cast integral types to scoped enums

Tags:

c++

c++11

C++11 scoped enums are great, you should use them whenever possible. However, sometimes you need to convert an integer to a scoped enum value (say if you are getting it from user input).

Is there a safe way to do this and detect when the value is not valid (i.e., outside the permitted values of the enum)?

I believe just using static_cast leads to undefined behavior if the integer is not valid. Is there a generic way of doing that does not require writing a conversion function for each scoped enum type by hand (and that needs to be updated every time you add a new value to the enum)?

like image 604
toth Avatar asked Jun 10 '16 15:06

toth


2 Answers

A common way to do that is to include in your enum an ending marker

enum class Colors : char
{
  Red,
  Green,
  Blue,
  Last_Element
}

Using this approach, when converting, you can check if the value you're using is less than the value of Last_Element.

Consider the following function:

template<typename T>
typename std::enable_if<std::is_enum<T>::value, bool>::type
  IsValidEnumIntegral<T>(int integral)
{
  return (int)(T::Last_Element) > integral;
}

and you could use it like so:

if(IsValidEnumIntegral<Colors>(2))
  //do whatever

This would work for any enum you made with an element called Last_Element. You could go further to create a similar function to then auto-convert for you.

Note: This is not tested. I am not in a position to do so at the moment but I think this could work.

EDIT: This will only work if the enum in question uses a set of integers with no gaps for its elements. The function provided will also assume the enum does not contain negative integers, though a First_Element can easily be added to it.

like image 111
Altainia Avatar answered Sep 19 '22 02:09

Altainia


[dcl.enum]/8:

For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type.

This includes all scoped enumerations, because the underlying type of a scoped enum defaults to int:

The underlying type can be explicitly specified using an enum-base. For a scoped enumeration type, the underlying type is int if it is not explicitly specified. In both of these cases, the underlying type is said to be fixed.

Thus, by verifying that the input value is within the range of the underlying type of the enum (which you can check with std::numeric_limits and std::underlying_type), you can be sure that the static_cast will always have well-defined behavior.

However, that's not enough if the rest of your program isn't prepared to handle every value within the range of the enum's underlying type. In that case, you'll have to do validation yourself, possibly with something along the lines of @Altainia's answer.

like image 29
T.C. Avatar answered Sep 22 '22 02:09

T.C.