Ist es möglich, die Kardinalität eines c ++ zu bestimmen enum class
:
enum class Example { A, B, C, D, E };
Ich habe versucht, es zu verwenden sizeof
, es gibt jedoch 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)?
c++
c++11
cardinality
enum-class
Bquenin
quelle
quelle
enum
undenum class
es sind sehr unterschiedliche Konzepte.enum class
Werte iterieren. Was wäre also von Vorteil, wenn Sie die Zahl kennen würden?Antworten:
Nicht direkt, aber Sie könnten den folgenden Trick verwenden:
enum class Example { A, B, C, D, E, Count };
Dann steht die Kardinalität als zur Verfügung
static_cast<int>(Example::Count)
.Dies funktioniert natürlich nur dann gut, 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, was sich nicht davon unterscheidet, dass Sie eine separate Konstante beibehalten müssen wie auch immer:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
Der einzige Nachteil ist, dass der Compiler es Ihnen ermöglicht,
Example::Count
als Argument für einen Aufzählungswert zu verwenden - seien Sie also vorsichtig, wenn Sie diesen verwenden! (Ich persönlich finde dies jedoch in der Praxis kein Problem.)quelle
enum class
es anstatt mit einfachenenum
s. Ich werde in einer Besetzung bearbeiten, um klar zu sein.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.Wo ist der Nachteil?
Diese Bibliothek verwendet einen compilerspezifischen Hack (basierend auf
__PRETTY_FUNCTION__
/__FUNCSIG__
), der mit Clang> = 5, MSVC> = 15.3 und GCC> = 9 funktioniert.Wir gehen den angegebenen Intervallbereich durch und finden alle Aufzählungen mit einem Namen. Dies ist ihre Anzahl. Lesen Sie mehr über Einschränkungen
Viele weitere Informationen zu diesem Hack finden Sie in diesem Beitrag unter https://taylorconor.com/blog/enum-reflection .
quelle
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 der Antwort von UglyCoder ab , verbessert sie jedoch auf drei Arten.
BEGIN
undSIZE
) ( Camerons Antwort hat auch dieses Problem.)Der Vorteil von UglyCoder gegenüber Camerons Antwort, dass Enumeratoren beliebige Werte zugewiesen werden können, bleibt erhalten .
Ein Problem (gemeinsam mit UglyCoder, aber nicht mit Cameron ) besteht darin, dass dadurch Zeilenumbrüche und Kommentare erheblich werden ... was unerwartet ist. So könnte jemand einen Eintrag mit Leerzeichen oder einen Kommentar hinzufügen, ohne
TEST_SIZE
die Berechnung anzupassen .quelle
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;
quelle
short
wäre,int
z. B. beim Erstellen eines Einheitsaufbaus erreicht werden. (Ich würde sagen, dies ist eher ein Problem mit Unity Builds als mit Ihrem vorgeschlagenen Trick.)Es gibt einen Trick, der auf X () basiert - Makros: Bild, Sie haben die folgende Aufzählung:
enum MyEnum {BOX, RECT};
Formatieren Sie es neu in:
#define MyEnumDef \ X(BOX), \ X(RECT)
Dann definiert der folgende Code den Aufzählungstyp:
enum MyEnum { #define X(val) val MyEnumDef #undef X };
Der folgende Code berechnet die Anzahl der Enum-Elemente:
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 ...
quelle
#define MyEnumDef
(und einfügen#define X(val) val
), wodurch Sie die Anzahl der Elemente mit just zählen können#define X(val) +1
constexpr std::size_t len = MyEnumDef;
.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 };
quelle
enum
s funktioniert dies nicht wie esExampleCount
istExample
. Um die Anzahl der Elemente in zu erhaltenExample
,ExampleCount
müsste in einen ganzzahligen Typ umgewandelt werden.Wenn Sie die Präprozessor-Dienstprogramme von boost verwenden, können Sie die Anzahl mithilfe von ermitteln
BOOST_PP_SEQ_SIZE(...)
.Zum Beispiel könnte man das
CREATE_ENUM
Makro wie folgt definieren:#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:
würde den folgenden Code erzeugen:
enum class Example : std::int32_t { A, B, C, D, E }; static constexpr std::int32_t ExampleCount = 5;
Dies kratzt nur die Oberfläche in Bezug auf die Boost-Präprozessor-Werkzeuge. Beispielsweise könnte Ihr Makro auch Dienstprogramme zur Zeichenfolgenkonvertierung und Ostream-Operatoren für Ihre stark typisierte Aufzählung definieren.
Weitere Informationen zu Boost-Präprozessor-Tools finden Sie hier: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html
Abgesehen davon stimme ich @FantasticMrFox nachdrücklich zu, dass der zusätzliche
Count
Aufzählungswert, der in der akzeptierten Antwort verwendet wird, bei Verwendung einerswitch
Anweisung unzählige Compiler- Warnprobleme verursacht . Ich finde dieunhandled case
Compiler-Warnung sehr nützlich für eine sicherere Code-Wartung, daher möchte ich sie nicht untergraben.quelle
Es kann durch einen Trick mit std :: initializer_list gelöst werden:
#define TypedEnum(Name, Type, ...) \ struct Name { \ enum : Type{ \ __VA_ARGS__ \ }; \ static inline const size_t count = []{ \ static Type __VA_ARGS__; return std::size({__VA_ARGS__}); \ }(); \ };
Verwendung:
#define Enum(Name, ...) TypedEnum(Name, int, _VA_ARGS_) Enum(FakeEnum, A = 1, B = 0, C) int main() { std::cout << FakeEnum::A << std::endl << FakeEnun::count << std::endl; }
quelle
Es gibt einen anderen Weg, der nicht auf Zeilenanzahl oder Vorlagen beruht. Die einzige Voraussetzung ist, die Enum-Werte in ihre eigene Datei zu stecken und den Präprozessor / Compiler dazu zu bringen, die Zählung wie folgt durchzuführen:
my_enum_inc.h
ENUMVAL(BANANA) ENUMVAL(ORANGE=10) ENUMVAL(KIWI) ... #undef ENUMVAL
my_enum.h
typedef enum { #define ENUMVAL(TYPE) TYPE, #include "my_enum_inc.h" } Fruits; #define ENUMVAL(TYPE) +1 const size_t num_fruits = #include "my_enum_inc.h" ;
Auf diese Weise können Sie Kommentare mit den Aufzählungswerten einfügen, Werte neu zuweisen und keinen ungültigen Aufzählungswert "count" einfügen, der im Code ignoriert / berücksichtigt werden muss.
Wenn Sie sich nicht für Kommentare interessieren, benötigen Sie keine zusätzliche Datei und können dies wie oben beschrieben tun, z.
#define MY_ENUM_LIST \ ENUMVAL(BANANA) \ ENUMVAL(ORANGE = 7) \ ENUMVAL(KIWI)
und ersetzen Sie die
#include "my_enum_inc.h"
Anweisungen durch MY_ENUM_LIST, aber Sie müssen#undef ENUMVAL
nach jeder Verwendung.quelle
Eine andere Art von "dummer" Lösung dafür ist:
enum class Example { A, B, C, D, E }; constexpr int ExampleCount = [] { Example e{}; int count = 0; switch (e) { case Example::A: count++; case Example::B: count++; case Example::C: count++; case Example::D: count++; case Example::E: count++; } return count; }();
Wenn Sie dies mit kompilieren
-Werror=switch
, stellen Sie sicher, dass Sie eine Compiler-Warnung erhalten, wenn Sie einen Switch-Fall weglassen oder duplizieren. Es ist auch constexpr, so dass dies zur Kompilierungszeit berechnet wird.Beachten Sie jedoch, dass selbst für ein en
enum class
der initialisierte Standardwert 0 ist, auch wenn der erste Wert der Aufzählung nicht 0 ist. Sie müssen also entweder mit 0 beginnen oder den ersten Wert explizit verwenden.quelle
Nein, du musst es in den Code schreiben.
quelle
Sie können auch überlegen,
static_cast<int>(Example::E) + 1
welches das zusätzliche Element eliminiert.quelle
Example::E
als letzter Wert in der Aufzählung ersetzt werden können. Auch wenn dies nicht der Fall ist,Example::E
kann sich der Literalwert ändern.Reflexion TS: statische Reflexion von Aufzählungen (und anderen Typen)
Reflection TS , insbesondere [Reflect.ops.enum] / 2 der neuesten Version des Reflection TS-Entwurfs, bietet folgende Möglichkeiten
get_enumerators
TransformationTrait
:[Reflect.ops.objseq] des Entwurfs behandelt
ObjectSequence
Operationen, wobei insbesondere [Reflect.ops.objseq] / 1 dasget_size
Merkmal zum Extrahieren der Anzahl von Elementen für ein Metaobjekt abdeckt , das Folgendes erfülltObjectSequence
:Wenn also in Reflection TS akzeptiert und in seiner aktuellen Form implementiert werden soll, kann die Anzahl der Elemente einer Aufzählung zur Kompilierungszeit wie folgt berechnet werden:
enum class Example { A, B, C, D, E }; using ExampleEnumerators = get_enumerators<Example>::type; static_assert(get_size<ExampleEnumerators>::value == 5U, "");
wo wir wahrscheinlich Alias-Vorlagen sehen
get_enumerators_v
undget_type_v
die Reflexion weiter vereinfachen:enum class Example { A, B, C, D, E }; using ExampleEnumerators = get_enumerators_t<Example>; static_assert(get_size_v<ExampleEnumerators> == 5U, "");
Status bei Reflexion TS
Wie im Reisebericht von Herb Sutter angegeben : Sommer-ISO-C ++ - Standardtreffen (Rapperswil) vom Sommertreffen des ISO-C ++ - Komitees am 9. Juni 2018 wurde Reflection TS als vollständig erklärt
und war ursprünglich für C ++ 20 geplant , aber es ist etwas unklar, ob Reflection TS noch die Chance hat, es in die C ++ 20-Version zu schaffen.
quelle