Continuous enum C++11
This is not possible in pure C++, because there is no way to enumerate the enum values, or discover the number of the values and minimum and maximum values. But you could try using the help of your compiler to implement something close to what you want. For example, in gcc it is possible to enforce a compilation error if a switch
statement does not handle all values of an enum:
enum class my_enum {
A = 0,
B = 1,
C = 2
};
#pragma GCC diagnostic push
#if __GNUC__ < 5
#pragma GCC diagnostic error "-Wswitch"
#else
#pragma GCC diagnostic error "-Wswitch-enum"
#endif
constexpr bool is_my_enum_continuous(my_enum t = my_enum())
{
// Check that we know all enum values. Effectively works as a static assert.
switch (t)
{
// Intentionally no default case.
// The compiler will give an error if not all enum values are listed below.
case my_enum::A:
case my_enum::B:
case my_enum::C:
break;
}
// Check that the enum is continuous
auto [min, max] = std::minmax({my_enum::A, my_enum::B, my_enum::C});
return static_cast< int >(min) == 0 && static_cast< int >(max) == 2;
}
#pragma GCC diagnostic pop
Obviously, this is specialized for a given enum, but definition of such functions can be automated with preprocessor.
For a number of enum
s you can probably hack your way through this using the Magic Enum library. For example:
#include "magic_enum.hpp"
template <typename Enum>
constexpr bool is_continuous(Enum = Enum{}) {
// make sure we're actually testing an enum
if constexpr (!std::is_enum_v<Enum>)
return false;
else {
// get a sorted list of values in the enum
const auto values = magic_enum::enum_values<Enum>();
if (std::size(values) == 0)
return true;
// for every value, either it's the same as the last one or it's one larger
auto prev = values[0];
for (auto x : values) {
auto next = static_cast<Enum>(magic_enum::enum_integer(prev) + 1);
if (x != prev && x != next)
return false;
else
prev = x;
}
return true;
}
}
Note that this is indeed, as the library name implies, "magic" – the library functions on a number of compiler-specific hacks. As such it doesn't really meet your requirement of "pure C++", but is probably as good as we can get until we have reflection facilities in the language.
All enum's are continuous. 0 is always allowed; the highest value allowed is the highest enumerator rounded up to the next 1<<N -1
(all bits one), and all values in between are allowed too. ([dcl.enum] 9.7.1/5). If there are negative enumerators defined, the lowest value allowed is similarly defined by rounding down the lowest enumerator.
The enumerators defined in the enum
are constant expressions with a value in range and the correct type, but you can define additional constants outside the enum
which have the same properties:
constexpr enum class Types_Discontinuous = static_cast<Types_Discontinuous>(2)