125 votes

Est-il possible de déterminer le nombre d'éléments d'une classe enum c++ ?

Est-il possible de déterminer la cardinalité d'un fichier 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 de l'enum.

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

Existe-t-il un moyen standard d'obtenir la cardinalité (5 dans mon exemple) ?

107voto

Cameron Points 32208

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 static_cast<int>(Example::Count) .

Bien sûr, cela ne fonctionne que si vous laissez les valeurs de l'enum être automatiquement attribuées, en commençant par 0. Si ce n'est pas le cas, vous pouvez attribuer manuellement la cardinalité correcte à Count, ce qui n'est pas vraiment différent de devoir maintenir une constante séparée de toute façon :

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 d'énumération -- donc soyez prudent si vous utilisez ceci ! (Je trouve personnellement que ce n'est pas un problème en pratique, cependant).

48voto

Neargye Points 1582

Pour C++17, vous pouvez utiliser magic_enum::enum_count de lib https://github.com/Neargye/magic_enum :

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

Où est l'inconvénient ?

Cette bibliothèque utilise un hack spécifique au compilateur (basé sur __PRETTY_FUNCTION__ / __FUNCSIG__ ), qui fonctionne sur Clang >= 5, MSVC >= 15.3 et GCC >= 9.

Nous parcourons l'intervalle donné, et trouvons toutes les énumérations avec un nom, ce sera leur nombre. Plus d'informations sur limitations

Plus d'informations sur ce hack dans ce post https://taylorconor.com/blog/enum-reflection .

34voto

Eponymous Points 577
// clang-format off
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;
// clang-format on

Elle est dérivée de Réponse de UglyCoder mais l'améliore de trois façons.

  • Il n'y a pas d'éléments supplémentaires dans le type_safe enum ( BEGIN y SIZE ) ( La réponse de Cameron a également ce problème).
    • Le compilateur ne se plaindra pas de leur absence dans une instruction switch (un problème important).
    • Ils ne peuvent pas être passés par inadvertance aux fonctions qui attendent votre enum. (problème peu courant)
  • Il ne nécessite pas de moulage pour être utilisé. ( La réponse de Cameron a aussi ce problème).
  • La soustraction ne modifie pas la taille du type de classe d'énumération.

Il conserve UglyCoder's avantage sur La réponse de Cameron que des valeurs arbitraires peuvent être attribuées aux énumérateurs.

Un problème (partagé avec UglyCoder 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 TEST_SIZE Le calcul de l'entreprise. Cela signifie que les formateurs de code peuvent le casser. Après evg656e's j'ai modifié la réponse pour désactiver clang-format mais caveat emptor si vous utilisez un formateur différent.

9voto

UglyCoder Points 199
// clang-format off
enum class TEST
{
    BEGIN = __LINE__
    , ONE
    , TWO
    , NUMBER = __LINE__ - BEGIN - 1
};
// clang-format on

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;

8voto

ixjxk Points 81

Il peut être résolu par une astuce avec std::initializer_list :

#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__}); \
    }();                                                          \
};

Uso:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X