Is there a way to check in C++11 if an enum is continuous?
It is fully valid to give an enum values which are not. Is there maybe a feature like a type trait in C++14, C++17 or maybe C++20 to check is the enum is continuous? This to be used in a static_assert.
A small example follows:
enum class Types_Discontinuous {
A = 10,
B = 1,
C = 100
};
enum class Types_Continuous {
A = 0,
B = 1,
C = 2
};
static_assert(SOME_TEST<Types_Discontinuous>::value, "Enum should be continuous"); // Fails
static_assert(SOME_TEST<Types_Continuous>::value, "Enum should be continuous"); // Passes
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.
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.
I'd love to see an answer on this. I've been needing it as well.
Unfortunately, I don't think this is possible using the existing utilities. If you want to implement a type trait on this, you need support from your compiler, so writing a template for it doesn't sound feasible.
I've already extended the enumeration with a specific tag to indicate it is contiguous and immediately gives you the size: enum class constructor c++ , how to pass specific value?
Alternatively, you can write your own trait:
template<T> struct IsContiguous : std::false_type {};
This needs to be specialized whenever you define an contiguous enum where you want to use this. Unfortunately, this requires some maintenance and attention if the enum gets changed.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With