Fortlaufende Aufzählung C ++ 11

17

Gibt es eine Möglichkeit, in C ++ 11 zu überprüfen, ob eine Aufzählung fortlaufend ist ?

Es ist voll gültig, eine Aufzählung anzugeben, die nicht sind. Gibt es möglicherweise eine Funktion wie ein Typmerkmal in C ++ 14, C ++ 17 oder C ++ 20, um zu überprüfen, ob die Aufzählung fortlaufend ist? Dies soll in einem static_assert verwendet werden.

Es folgt ein kleines Beispiel:

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
Bart
quelle
1
Bedeutet weiterhin, dass es eine aufsteigende Reihenfolge hat oder dass es mit Null beginnt und dann +1 für jeden Wert?
RoQuOTriX
5
Es gibt keine Möglichkeit, Aufzählungsbezeichnungen aufzulisten, daher ist dies nicht innerhalb des Programms selbst möglich.
Ein Programmierer
1
Interessant. Ich denke im Sinne der Vorlagenprogrammierung darüber nach, wie Sie einen Compiler dazu bringen können, eine Fakultät zu berechnen. Sie würden die Sache mit den beiden Grenzen A und C beginnen, und die Vorlagenfunktionen prüfen über SFINAE, ob alle Werte zwischen ihnen in der oder in der anderen vorhanden sind enum. Leider habe ich einen Tagesjob und kann daher nicht versuchen, dies aufzuschreiben, obwohl ich eine Antwort auf der Grundlage dieses Ansatzes positiv bewerten werde. Ich bin mir ziemlich sicher, dass jemand wie @barry oder @sehe das könnte.
Bathseba
1
@RoQuOTriX Wie würden Sie einen Wert einem Label zuordnen? Und wie würden Sie die Reihenfolge der Etiketten überprüfen? Und wie könnte es zur Kompilierungszeit gemacht werden (wofür benötigt wird static_assert)? Auch wenn Sie keine "schöne Lösung" finden können, schreiben Sie bitte trotzdem eine Antwort, da ich sehr gespannt bin, wie dies generisch gemacht werden könnte.
Ein Programmierer
1
@Someprogrammerdude was Sie beschrieben haben, ist die "schöne" oder gute Lösung. Was ich meinte, war die "einfache" Check-Lösung, die Sie für jede Aufzählung und jeden Segen neu schreiben müssten. Ich hoffe, niemand tut das
RoQuOTriX

Antworten:

7

Für eine Reihe von enums können Sie sich wahrscheinlich mithilfe der Magic Enum- Bibliothek durch diese Sache hacken . Zum Beispiel:

#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;
    }
}

Beachten Sie, dass dies tatsächlich, wie der Name der Bibliothek andeutet, "Magie" ist - die Bibliothek funktioniert bei einer Reihe von compilerspezifischen Hacks. Als solches erfüllt es nicht wirklich Ihre Anforderungen an "reines C ++", ist aber wahrscheinlich so gut wie möglich, bis wir Reflexionsmöglichkeiten in der Sprache haben.

N. Shead
quelle
Es ist in der Tat magisch, aber das würde meiner Situation am besten entsprechen.
Bart
7

Dies ist in reinem C ++ nicht möglich, da es keine Möglichkeit gibt, die Aufzählungswerte aufzulisten oder die Anzahl der Werte sowie die Minimal- und Maximalwerte zu ermitteln. Sie können jedoch versuchen, mithilfe Ihres Compilers etwas zu implementieren, das Ihren Wünschen entspricht. In gcc ist es beispielsweise möglich, einen Kompilierungsfehler zu erzwingen, wenn eine switchAnweisung nicht alle Werte einer Aufzählung verarbeitet:

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

Dies ist natürlich auf eine bestimmte Aufzählung spezialisiert, aber die Definition solcher Funktionen kann mit einem Präprozessor automatisiert werden.

Andrey Semashev
quelle
Wenn ich das richtig verstehe, müsste ich trotzdem alle Enum-Werte in den Schalter und die Liste für minmax schreiben. Derzeit habe ich mehrere Aufzählungen, so dass dies zwar möglich, aber für meine Situation nicht bevorzugt ist.
Bart
1

Ich würde gerne eine Antwort darauf sehen. Ich habe es auch gebraucht.

Leider glaube ich nicht, dass dies mit den vorhandenen Dienstprogrammen möglich ist. Wenn Sie hierfür ein Typmerkmal implementieren möchten, benötigen Sie Unterstützung von Ihrem Compiler, sodass das Schreiben einer Vorlage dafür nicht möglich klingt.

Ich habe die Aufzählung bereits um ein bestimmtes Tag erweitert, um anzuzeigen, dass sie zusammenhängend ist, und gibt Ihnen sofort die Größe: Enum-Klassenkonstruktor c ++, wie kann ein bestimmter Wert übergeben werden?

Alternativ können Sie Ihr eigenes Merkmal schreiben:

 template<T> struct IsContiguous : std::false_type {};

Dies muss immer dann spezialisiert werden, wenn Sie eine zusammenhängende Aufzählung definieren, in der Sie diese verwenden möchten. Leider erfordert dies einige Wartung und Aufmerksamkeit, wenn die Aufzählung geändert wird.

JVApen
quelle
1
Sie könnten eine
Codeprüfung
Ja, in der Tat. Wenn Sie die Fähigkeit haben, es zu schreiben.
JVApen
1

Alle Aufzählungen sind fortlaufend. 0 ist immer erlaubt; Der höchste zulässige Wert ist der höchste auf den nächsten aufgerundete Enumerator 1<<N -1(alle Bits eins), und alle dazwischen liegenden Werte sind ebenfalls zulässig. ([dcl.enum] 9.7.1 / 5). Wenn negative Enumeratoren definiert sind, wird der niedrigste zulässige Wert auf ähnliche Weise definiert, indem der niedrigste Enumerator abgerundet wird.

Die in definierten Enumeratoren enumsind konstante Ausdrücke mit einem Wert im Bereich und dem richtigen Typ. Sie können jedoch zusätzliche Konstanten außerhalb der definieren, enumdie dieselben Eigenschaften haben:

constexpr enum class Types_Discontinuous = static_cast<Types_Discontinuous>(2)

MSalters
quelle
2
Obwohl Sie richtig liegen, ist aus dem OP klar, dass wir dies für die definierten Werte wissen wollen. (PS: Die Abstimmung ist nicht meine)
JVApen
1
@JVApen: Das ist genau das Problem. Die "definierten Werte" sind keine Eigenschaft des Aufzählungstyps selbst. Der Standard gibt explizit die Werte der Aufzählung an.
MSalters