Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Continuous enum C++11

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
like image 793
Bart Avatar asked Feb 06 '20 11:02

Bart


Video Answer


4 Answers

For a number of enums 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.

like image 154
N. Shead Avatar answered Oct 20 '22 00:10

N. Shead


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.

like image 22
Andrey Semashev Avatar answered Oct 19 '22 22:10

Andrey Semashev


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.

like image 23
JVApen Avatar answered Oct 19 '22 22:10

JVApen


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)

like image 33
MSalters Avatar answered Oct 19 '22 23:10

MSalters