webentwicklung-frage-antwort-db.com.de

Ist es möglich, die Anzahl der Elemente einer C ++ - Aufzählungsklasse zu bestimmen?

Ist es möglich, die Kardinalität eines c ++ enum class:

enum class Example { A, B, C, D, E };

Ich habe versucht, sizeof zu verwenden, aber es gibt die Größe eines Enum-Elements zurück.

sizeof(Example); // Returns 4 (on my architecture)

Gibt es einen Standardweg, um die Kardinalität zu erhalten (5 in meinem Beispiel)?

59
bquenin

Nicht direkt, aber du könntest den folgenden Trick benutzen:

enum class Example { A, B, C, D, E, Count };

Die Kardinalität steht dann als (int)Example::Count Zur Verfügung.

Dies funktioniert natürlich nur dann, wenn Sie die Werte der Aufzählung ab 0 automatisch zuweisen lassen. Wenn dies nicht der Fall ist, können Sie Count manuell die richtige Kardinalität zuweisen wie auch immer:

enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };

Der einzige Nachteil ist, dass Sie mit dem Compiler Example::Count Als Argument für einen Enum-Wert verwenden können - seien Sie also vorsichtig, wenn Sie diesen verwenden! (Ich persönlich finde dies jedoch kein Problem in der Praxis.)

54
Cameron
constexpr auto TEST_START_LINE = __LINE__;
enum class TEST { // Subtract extra lines from TEST_SIZE if an entry takes more than one 
    ONE = 7
  , TWO = 6
  , THREE = 9
};
constexpr auto TEST_SIZE = __LINE__ - TEST_START_LINE - 3;

Dies leitet sich aus glyCoders Antwort ab, verbessert es jedoch auf drei Arten.

  • Es gibt keine zusätzlichen Elemente in der type_safe-Aufzählung (BEGIN und SIZE) ( Camerons Antwort hat auch dieses Problem.)
    • Der Compiler wird sich nicht darüber beschweren, dass sie in einer switch-Anweisung fehlen (ein erhebliches Problem).
    • Sie können nicht versehentlich an Funktionen übergeben werden, die Ihre Aufzählung erwarten. (kein häufiges Problem)
  • Für die Verwendung ist kein Gießen erforderlich. ( Camerons Antwort hat auch dieses Problem.)
  • Die Subtraktion wirkt sich nicht auf die Größe des Aufzählungsklassentyps aus.

Es behält den Vorteil von glyCoder gegenüber Camerons Antwort , dass Enumeratoren beliebige Werte zugewiesen werden können.

Ein Problem (geteilt mit glyCoder , aber nicht mit Cameron ) ist, dass Zeilenumbrüche und Kommentare signifikant werden ... was unerwartet ist. So könnte jemand einen Eintrag mit Leerzeichen oder einem Kommentar hinzufügen, ohne TEST_SIZE 's Berechnung.

16
Eponymous

Für C++ 17 können Sie magic_enum::enum_count Von lib https://github.com/Neargye/magic_enum verwenden:

magic_enum::enum_count<Example>() -> 4.

7
Neargye
enum class TEST
{
    BEGIN = __LINE__
    , ONE
    , TWO
    , NUMBER = __LINE__ - BEGIN - 1
};

auto const TEST_SIZE = TEST::NUMBER;

// or this might be better 
constexpr int COUNTER(int val, int )
{
  return val;
}

constexpr int E_START{__COUNTER__};
enum class E
{
    ONE = COUNTER(90, __COUNTER__)  , TWO = COUNTER(1990, __COUNTER__)
};
template<typename T>
constexpr T E_SIZE = __COUNTER__ - E_START - 1;
7
UglyCoder

Ein Trick, den Sie versuchen können, besteht darin, am Ende Ihrer Liste einen Aufzählungswert hinzuzufügen und diesen als Größe zu verwenden. In deinem Beispiel

enum class Example { A, B, C, D, E, ExampleCount };
3
David Nehme

Nein, du musst es in den Code schreiben.

2
user1944441

Es gibt einen Trick, der auf X () - Makros basiert: image, Sie haben die folgende Aufzählung:

enum MyEnum {BOX, RECT};

Formatieren Sie es neu zu:

#define MyEnumDef \
    X(BOX), \
    X(RECT)

Der folgende Code definiert dann den Aufzählungstyp:

enum MyEnum
{
#define X(val) val
    MyEnumDef
#undef X
};

Und der folgende Code berechnet die Anzahl der Aufzählungselemente:

template <typename ... T> void null(T...) {}

template <typename ... T>
constexpr size_t countLength(T ... args)
{
    null(args...); //kill warnings
    return sizeof...(args);
}

constexpr size_t enumLength()
{
#define XValue(val) #val
    return countLength(MyEnumDef);
#undef XValue
}

...
std::array<int, enumLength()> some_arr; //enumLength() is compile-time
std::cout << enumLength() << std::endl; //result is: 2
...
2
Kirill Suetnov

Sie können auch static_cast<int>(Example::E) + 1 in Betracht ziehen, wodurch das zusätzliche Element eliminiert wird.

1
Fabio A. Correa

Wenn Sie die Präprozessor-Dienstprogramme von boost verwenden, können Sie die Anzahl mit BOOST_PP_SEQ_SIZE(...) ermitteln.

Zum Beispiel könnte man das CREATE_ENUM Makro wie folgt:

#include <boost/preprocessor.hpp>

#define ENUM_PRIMITIVE_TYPE std::int32_t

#define CREATE_ENUM(EnumType, enumValSeq)                                  \
enum class EnumType : ENUM_PRIMITIVE_TYPE                                  \
{                                                                          \
   BOOST_PP_SEQ_ENUM(enumValSeq)                                           \
};                                                                         \
static constexpr ENUM_PRIMITIVE_TYPE EnumType##Count =                     \
                 BOOST_PP_SEQ_SIZE(enumValSeq);                            \
// END MACRO   

Rufen Sie dann das Makro auf:

CREATE_ENUM(Example, (A)(B)(C)(D)(E));

würde den folgenden Code generieren:

enum class Example : std::int32_t 
{
   A, B, C, D, E 
};
static constexpr std::int32_t ExampleCount = 5;

Dies zerkratzt nur die Oberfläche in Bezug auf die Boost-Präprozessor-Werkzeuge. Zum Beispiel könnte Ihr Makro auch Dienstprogramme für die Konvertierung von Zeichenfolgen und ostream-Operatoren für Ihre stark typisierte Aufzählung definieren.

Weitere Informationen zu Boost-Präprozessortools finden Sie hier: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html


Abgesehen davon stimme ich mit @FantasticMrFox sehr überein, dass der zusätzliche Count Aufzählungswert, der in der akzeptierten Antwort verwendet wird, in Hülle und Fülle Compiler-Warnungen auslöst, wenn ein switch Erklärung. Ich finde die unhandled case Compiler-Warnung, die für eine sicherere Codewartung sehr nützlich ist, damit ich sie nicht untergraben möchte.

0
arr_sea