Est-il possible de déterminer la cardinalité d'un c ++ enum class
:
enum class Example { A, B, C, D, E };
J'ai essayé d'utiliser sizeof
, cependant, il renvoie la taille d'un élément enum.
sizeof(Example); // Returns 4 (on my architecture)
Existe-t-il un moyen standard d'obtenir la cardinalité (5 dans mon exemple)?
Pas directement, mais vous pouvez utiliser l'astuce suivante:
enum class Example { A, B, C, D, E, Count };
La cardinalité est alors disponible sous la forme (int)Example::Count
.
Bien sûr, cela ne fonctionne bien que si vous laissez les valeurs de l'énumération attribuées automatiquement, à partir de 0. Si ce n'est pas le cas, vous pouvez attribuer manuellement la cardinalité correcte à Count, ce qui n'est vraiment pas différent d'avoir à maintenir une constante distincte en tous cas:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
Le seul inconvénient est que le compilateur vous permettra d'utiliser Example::Count
comme argument pour une valeur enum - soyez donc prudent si vous l'utilisez! (Je trouve personnellement que ce n'est pas un problème dans la pratique, cependant.)
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;
Ceci est dérivé de réponse d'UglyCoder mais l'améliore de trois manières.
BEGIN
et SIZE
) ( la réponse de Cameron a également ce problème.) Il conserve glyCoder avantage sur réponse de Cameron que les énumérateurs peuvent se voir attribuer des valeurs arbitraires.
Un problème (partagé avec glyCoder mais pas avec Cameron ) est qu'il rend les nouvelles lignes et les commentaires significatifs ... ce qui est inattendu. Ainsi, quelqu'un pourrait ajouter une entrée avec un espace ou un commentaire sans ajuster le calcul de TEST_SIZE
.
Pour C++ 17, vous pouvez utiliser magic_enum::enum_count
De lib https://github.com/Neargye/magic_enum :
magic_enum::enum_count<Example>()
-> 4.
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;
Une astuce que vous pouvez essayer est d'ajouter une valeur d'énumération à la fin de votre liste et de l'utiliser comme taille. Dans votre exemple
enum class Example { A, B, C, D, E, ExampleCount };
Non, vous devez l'écrire dans le code.
Il existe une astuce basée sur X () - macros: image, vous avez l'énumération suivante:
enum MyEnum {BOX, RECT};
Reformatez-le pour:
#define MyEnumDef \
X(BOX), \
X(RECT)
Ensuite, le code suivant définit le type d'énumération:
enum MyEnum
{
#define X(val) val
MyEnumDef
#undef X
};
Et le code suivant calcule le nombre d'éléments enum:
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
...
Vous pouvez également considérer static_cast<int>(Example::E) + 1
qui élimine l'élément supplémentaire.
Si vous utilisez les utilitaires de préprocesseur de boost, vous pouvez obtenir le nombre en utilisant BOOST_PP_SEQ_SIZE(...)
.
Par exemple, on pourrait définir le CREATE_ENUM
macro comme suit:
#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
Ensuite, appelez la macro:
CREATE_ENUM(Example, (A)(B)(C)(D)(E));
générerait le code suivant:
enum class Example : std::int32_t
{
A, B, C, D, E
};
static constexpr std::int32_t ExampleCount = 5;
Cela ne fait qu'effleurer la surface en ce qui concerne les outils de préprocesseur de boost. Par exemple, votre macro peut également définir des utilitaires de conversion de/vers et des opérateurs ostream pour votre énumération fortement typée.
Plus d'informations sur les outils de préprocesseur boost ici: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html
En passant, je suis fortement d'accord avec @FantasticMrFox que la valeur énumérée Count
supplémentaire utilisée dans la réponse acceptée créera de nombreux maux de tête d'avertissement du compilateur si vous utilisez un switch
déclaration. Je trouve le unhandled case
avertissement du compilateur assez utile pour une maintenance plus sûre du code, donc je ne voudrais pas le miner.