it-swarm.com.de

Wie kann man überprüfen, ob der Aufzählungswert gültig ist?

Ich lese einen enum-Wert aus einer Binärdatei und möchte prüfen, ob der Wert wirklich ein Teil der enum-Werte ist. Wie kann ich es tun?

#include <iostream>

enum Abc
{
    A = 4,
    B = 8,
    C = 12
};

int main()
{
    int v1 = 4;
    Abc v2 = static_cast< Abc >( v1 );

    switch ( v2 )
    {
        case A:
            std::cout<<"A"<<std::endl;
            break;
        case B:
            std::cout<<"B"<<std::endl;
            break;
        case C:
            std::cout<<"C"<<std::endl;
            break;
        default :
            std::cout<<"no match found"<<std::endl;
    }
}

Muss ich den Operator switch verwenden oder gibt es einen besseren Weg?

EDIT

Ich habe enum Werte gesetzt und kann sie leider nicht ändern. Zu allem Überfluss sind sie nicht stetig (ihre Werte gehen auf 0, 75,76,80,85,90,95,100 usw.)

38
BЈовић

enum value ist in C++ gültig, wenn er in den Bereich [A, B] fällt, der durch die folgende Standardregel definiert wird. Im Fall von enum X { A = 1, B = 3 } wird der Wert von 2 als gültiger Aufzählungswert betrachtet.

Betrachten Sie 7,2/6 des Standards:

Bei einer Enumeration, bei der emin der kleinste Enumerator und emax der größte ist, sind die Werte der Enumeration die Werte des zugrunde liegenden Typs im Bereich von bmin bis bmax, wobei bmin und bmax die kleinsten und größten Werte sind des kleinsten Bitfeldes, das emin und emax speichern kann. Es ist möglich, eine Aufzählung zu definieren, deren Werte von keinem ihrer Enumeratoren definiert wurden.

Es gibt keine Rückblicke in C++. Ein Ansatz, der zu ergreifen ist, besteht darin, zusätzlich Enumerationswerte in einem Array aufzulisten und einen Wrapper zu schreiben, der die Konvertierung durchführt und bei einem Fehler möglicherweise eine Ausnahme auslöst.

Siehe Ähnliche Frage , um zu erfahren, wie Sie int in enum auflisten können.

21
Leonid

Vielleicht verwenden Sie Enummen wie folgt:

enum MyEnum
{
A,
B,
C
};

und zu überprüfen 

if (v2 >= A && v2 <= C)

Wenn Sie keine Werte für Aufzählungskonstanten angeben, beginnen die Werte bei Null und werden mit jedem Abwärtsschritt in der Liste um eins erhöht. Beispiel: enum MyEnumType { ALPHA, BETA, GAMMA }; ALPHA hat einen Wert von 0, BETA hat einen Wert von 1 und GAMMA hat einen Wert von 2.

9
Andrew

Die einzige Möglichkeit, es "einfach" zu machen, bestand darin, ein sortiertes Array der Enumerationen (Makro) zu erstellen und dies zu überprüfen.

Der switch-Trick schlägt mit enums fehl, da eine enum mehr als einen Enumerator mit einem bestimmten Wert haben kann.

Es ist wirklich ein nerviges Thema.

7
Matthieu M.

In C++ 11 gibt es eine bessere Möglichkeit, wenn Sie bereit sind, Ihre Aufzählungswerte als Vorlagenparameter aufzulisten. Sie können dies als eine gute Sache betrachten, die es Ihnen ermöglicht, Teilmengen der gültigen Aufzählungswerte in verschiedenen Kontexten zu akzeptieren. häufig nützlich, wenn Codes aus externen Quellen analysiert werden.

Eine nützliche sinnvolle Ergänzung zu dem folgenden Beispiel wären einige statische Assertions bezüglich des zugrunde liegenden EnumType-Typs relativ zu IntType, um Abschneidungsprobleme zu vermeiden. Links als Übung.

#include <stdio.h>

template<typename EnumType, EnumType... Values> class EnumCheck;

template<typename EnumType> class EnumCheck<EnumType>
{
public:
    template<typename IntType>
    static bool constexpr is_value(IntType) { return false; }
};

template<typename EnumType, EnumType V, EnumType... Next>
class EnumCheck<EnumType, V, Next...> : private EnumCheck<EnumType, Next...>
{
    using super = EnumCheck<EnumType, Next...>;

public:
    template<typename IntType>
    static bool constexpr is_value(IntType v)
    {
        return v == static_cast<IntType>(V) || super::is_value(v);
    }
};

enum class Test {
    A = 1,
    C = 3,
    E = 5
};

using TestCheck = EnumCheck<Test, Test::A, Test::C, Test::E>;

void check_value(int v)
{
    if (TestCheck::is_value(v))
        printf("%d is OK\n", v);
    else
        printf("%d is not OK\n", v);
}

int main()
{
    for (int i = 0; i < 10; ++i)
        check_value(i);
}
7
janm

Managed Extensions for C++ unterstützt die folgende Syntax:

enum Abc
{
    A = 4,
    B = 8,
    C = 12
};

Enum::IsDefined(Abc::typeid, 8);

Referenz: MSDN " Managed Extensions for C++ Programming "

3
Brett

Wenn man über eine Sprache spricht, gibt es keinen besseren Weg. Die Aufzählungswerte existieren nur zur Kompilierzeit und es gibt keine Möglichkeit, sie programmatisch aufzuzählen. Mit einer durchdachten Infrastruktur können Sie jedoch möglicherweise immer noch alle Werte mehrfach auflisten. Siehe Einfache Möglichkeit, Variablen von Aufzählungstypen als Zeichenfolge in C zu verwenden.

Ihr Beispiel kann dann mit der dort angegebenen "enumFactory.h" neu geschrieben werden:

#include "enumFactory.h"

#define ABC_ENUM(XX) \
    XX(A,=4) \
    XX(B,=8) \
    XX(C,=12) \

DECLARE_ENUM(Abc,ABC_ENUM)

int main()
{
    int v1 = 4;
    Abc v2 = static_cast< Abc >( v1 );

    #define CHECK_ENUM_CASE(name,assign) case name: std::cout<< #name <<std::endl; break;
    switch ( v2 )
    {
        ABC_ENUM(CHECK_ENUM_CASE)
        default :
            std::cout<<"no match found"<<std::endl;
    }
    #undef CHECK_ENUM_CASE
}

oder sogar (mithilfe einiger weiterer in diesem Header vorhandener Funktionen):

#include "enumFactory.h"

#define ABC_ENUM(XX) \
    XX(A,=4) \
    XX(B,=8) \
    XX(C,=12) \

DECLARE_ENUM(Abc,ABC_ENUM)
DEFINE_ENUM(Abc,ABC_ENUM)

int main()
{
    int v1 = 4;
    Abc v2 = static_cast< Abc >( v1 );
    const char *name = GetString(v2);
    if (name[0]==0) name = "no match found";
    std::cout << name << std::endl;
}
2
Suma

Etwas necro, aber ... führt eine RANGE-Überprüfung von int in den ersten/letzten Enumerationswert durch (kann mit Janans Idee kombiniert werden, um genaue Prüfungen durchzuführen)

Header:

namespace chkenum
{
    template <class T, T begin, T end>
    struct RangeCheck
    {
    private:
        typedef typename std::underlying_type<T>::type val_t;
    public:
        static
        typename std::enable_if<std::is_enum<T>::value, bool>::type
        inrange(val_t value)
        {
            return value >= static_cast<val_t>(begin) && value <= static_cast<val_t>(end);
        }
    };

    template<class T>
    struct EnumCheck;
}

#define DECLARE_ENUM_CHECK(T,B,E) namespace chkenum {template<> struct EnumCheck<T> : public RangeCheck<T, B, E> {};}

template<class T>
inline
typename std::enable_if<std::is_enum<T>::value, bool>::type
testEnumRange(int val)
{
    return chkenum::EnumCheck<T>::inrange(val);
}

Aufzählungserklärung:

enum MinMaxType
{
     Max = 0x800, Min, Equal
};
DECLARE_ENUM_CHECK(MinMaxType, MinMaxType::Max, MinMaxType::Equal);

Verwendungszweck:

bool r = testEnumRange<MinMaxType>(i);

Im Unterschied zum oben genannten wird hauptsächlich vorgeschlagen, dass die Testfunktion nur vom Enummentyp selbst abhängig ist.

2
Alex Zaharov

Noch eine andere Möglichkeit:

#include <algorithm>
#include <iterator>
#include <iostream>

template<typename>
struct enum_traits { static constexpr void* values = nullptr; };

namespace detail
{

template<typename T>
constexpr bool is_value_of(int, void*) { return false; }

template<typename T, typename U>
constexpr bool is_value_of(int v, U)
{
    using std::begin; using std::end;

    return std::find_if(begin(enum_traits<T>::values), end(enum_traits<T>::values),
        [=](auto value){ return value == static_cast<T>(v); }
    ) != end(enum_traits<T>::values);
}

}

template<typename T>
constexpr bool is_value_of(int v)
{ return detail::is_value_of<T>(v, decltype(enum_traits<T>::values) { }); }

////////////////////
enum Abc { A = 4, B = 8, C = 12 };

template<>
struct enum_traits<Abc> { static constexpr auto values = { A, B, C }; };
decltype(enum_traits<Abc>::values) enum_traits<Abc>::values;

enum class Def { D = 1, E = 3, F = 5 };

int main()
{
    std::cout << "Abc:";
    for(int i = 0; i < 10; ++i)
        if(is_value_of<Abc>(i)) std::cout << " " << i;
    std::cout << std::endl;

    std::cout << "Def:";
    for(int i = 0; i < 10; ++i)
        if(is_value_of<Def>(i)) std::cout << " " << i;
    std::cout << std::endl;

    return 0;
}

Der "hässliche" Teil dieses Ansatzes muss IMHO definieren:

decltype(enum_traits<Abc>::values) enum_traits<Abc>::values

Wenn Sie Makros nicht ablehnen, können Sie es in ein Makro einschließen:

#define REGISTER_ENUM_VALUES(name, ...) \
template<> struct enum_traits<name> { static constexpr auto values = { __VA_ARGS__ }; }; \
decltype(enum_traits<name>::values) enum_traits<name>::values;
0