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) ?

6voto

Kirill Suetnov Points 76

Il existe une astuce basée sur les macros X()- : image, vous avez l'énumération suivante :

enum MyEnum {BOX, RECT};

Reformatez-le en :

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

Ensuite, le code suivant définit le type d'enum :

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

Et le code suivant calcule le nombre d'éléments de l'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
...

6voto

Zitrax Points 3443

Une autre sorte de solution "stupide" à ce problème est :

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

En compilant ceci avec -Werror=switch vous vous assurez d'obtenir un avertissement du compilateur si vous omettez ou dupliquez un switch case. C'est aussi constexpr, donc cela est calculé au moment de la compilation.

Mais notez que même pour un enum class la valeur initialisée par défaut est 0 même si la première valeur de l'enum n'est pas 0. Vous devez donc soit commencer à 0, soit utiliser explicitement la première valeur.

6voto

dfri Points 11222

Reflection TS : réflexion statique des enums (et autres types)

Réflexion TS en particulier [reflect.ops.enum]/2 de l'interface utilisateur. dernière version du projet Reflection TS offre le get_enumerators TransformationTrait fonctionnement :

[reflect.ops.enum]/2

template <Enum T> struct get_enumerators

Toutes les spécialisations de get_enumerators<T> doit respecter les TransformationTrait (20.10.1). Le type imbriqué nommé type désigne un type de méta-objet répondant aux critères suivants ObjectSequence contenant des éléments qui satisfont Enumerator et reflètent les énumérateurs du type d'énumération reflété par T .

[reflect.ops.objseq] de l'ébauche couvre ObjectSequence où, en particulier, [reflect.ops.objseq]/1 couvre la get_size trait pour l'extraction du nombre d'éléments pour un méta-objet satisfaisant ObjectSequence :

[reflect.ops.objseq]/1

template <ObjectSequence T> struct get_size;

Toutes les spécialisations de get_size<T> doit respecter les UnaryTypeTrait (20.10.1) dont la caractéristique de base est integral_constant<size_t, N> donde N est le nombre d'éléments dans la séquence d'objets.

Ainsi, si Reflection TS devait être accepté et mis en œuvre dans sa forme actuelle, le nombre d'éléments d'un enum peut être calculé, au moment de la compilation, comme suit :

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

using ExampleEnumerators = get_enumerators<Example>::type;

static_assert(get_size<ExampleEnumerators>::value == 5U, "");

où nous sommes susceptibles de voir des modèles d'alias get_enumerators_v y get_type_v pour simplifier davantage la réflexion :

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

using ExampleEnumerators = get_enumerators_t<Example>;

static_assert(get_size_v<ExampleEnumerators> == 5U, "");

État d'avancement de la réflexion TS

Comme indiqué par Herb Sutter Rapport de voyage : Réunion d'été de l'ISO sur les normes C++ (Rapperswil) à la suite de la réunion d'été du comité ISO C++ du 9 juin 2018, Reflection TS a été déclaré comme étant une fonctionnalité complète.

Reflection TS est complet en termes de fonctionnalités : La TS Reflection a été déclarée feature-complete et est envoyée pour son principal vote de commentaires au cours de l'été. Notez une fois de plus que le modèle actuel de syntaxe basé sur la métaprogrammation de la TS n'est qu'un substitut ; les commentaires demandés portent sur les " entrailles " de la conception, et le comité sait déjà qu'il a l'intention de remplacer la syntaxe de surface par un modèle de programmation plus simple qui utilise le code ordinaire de compilation et non pas le langage de programmation. <> -de la métaprogrammation.

et était initialement prévu pour C++20 Mais il n'est pas certain que Reflection TS ait encore une chance de figurer dans la version C++20.

4voto

David Nehme Points 11564

Une astuce que vous pouvez essayer est d'ajouter une valeur enum à la fin de votre liste et de l'utiliser comme taille. Dans votre exemple

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

3voto

arr_sea Points 231

Si vous utilisez les utilitaires du préprocesseur de boost, vous pouvez obtenir le compte en utilisant BOOST_PP_SEQ_SIZE(...) .

Par exemple, on pourrait définir le CREATE_ENUM 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, on appelle 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;

Ceci ne fait qu'effleurer la surface des outils du préprocesseur Boost. Par exemple, votre macro pourrait également définir des utilitaires de conversion de chaîne de caractères et des opérateurs ostream pour votre enum fortement typée.

Pour en savoir plus sur les outils du préprocesseur Boost, cliquez ici : https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html


Soit dit en passant, je suis tout à fait d'accord avec @FantasticMrFox pour dire que l'ajout d'un élément supplémentaire à l'offre de services de l'entreprise est une bonne chose. Count employée dans la réponse acceptée créera une multitude d'avertissements du compilateur si l'on utilise une valeur de type switch déclaration. Je trouve que le unhandled case L'avertissement du compilateur est très utile pour une maintenance plus sûre du code, et je ne voudrais pas le compromettre.

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