(Comment) puis-je compter les éléments dans une énumération?

98

Cette question m'est venue à l'esprit, quand j'avais quelque chose comme

enum Folders {FA, FB, FC};

et voulait créer un tableau de conteneurs pour chaque dossier:

ContainerClass*m_containers[3];
....
m_containers[FA] = ...; // etc.

( En utilisant des cartes , il est beaucoup plus élégante à l' utilisation: std::map<Folders, ContainerClass*> m_containers;)

Mais pour revenir à ma question initiale: que se passe-t-il si je ne souhaite pas coder en dur la taille du tableau, existe-t-il un moyen de déterminer le nombre d'éléments dans les dossiers? (Sans compter sur par exempleFC être le dernier élément de la liste, ce qui permettrait quelque chose comme ContainerClass*m_containers[FC+1]si je ne me trompais pas.)

fuenfundachtzig
la source
Cet article peut répondre à votre question: stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c .
StackedCrooked le
1
La question est un peu sous-spécifiée. Selon la norme C ++, int(FA) | int(FB) | int (FC)est également une valeur légale pour une Foldersvariable. Si vous effectuez un dimensionnement de m_containerssorte qu'une Foldersvariable soit un index valide, [FC+1]ce ne serait pas assez grand.
MSalters
J'ai demandé une chose très liée sur stackoverflow.com/questions/12972317/count-on-enum-c-automatic
sergiol
Je vous recommanderais la solution de stackoverflow.com/a/60216003/12894563 et une variante améliorée de stackoverflow.com/a/60271408/12894563
ixjxk

Réponses:

123

Il n'y a pas vraiment de bon moyen de faire cela, généralement vous voyez un élément supplémentaire dans l'énumération, c'est-à-dire

enum foobar {foo, bar, baz, quz, FOOBAR_NR_ITEMS};

Alors vous pouvez faire:

int fuz[FOOBAR_NR_ITEMS];

Toujours pas très gentil.

Mais bien sûr, vous réalisez que seul le nombre d'éléments dans une énumération n'est pas sûr, étant donné par exemple

enum foobar {foo, bar = 5, baz, quz = 20};

le nombre d'éléments serait de 4, mais les valeurs entières des valeurs d'énumération seraient bien en dehors de la plage d'index du tableau. L'utilisation de valeurs d'énumération pour l'indexation de tableau n'est pas sûre, vous devez envisager d'autres options.

edit: comme demandé, a fait ressortir l'entrée spéciale.

qui
la source
27
Appelez-le LAST ou ALWAYS_AT_END ou quelque chose de pas si énigmatique. Faites-le ressortir . Pour que les responsables suivants n'ajoutent pas accidentellement de nouvelles entrées après la fin du marqueur.
Martin York
3
Pour le meilleur ou pour le pire, c'est l'approche que nous adoptons dans notre organisation. Nous l'appelons normalement FINAL_enumname_ENTRY, tel que FINAL_foobar_ENTRY. J'ai également vu des gens utiliser une variable statique const FOOBAR_COUNT distincte définie juste après la déclaration enum, une approche légèrement plus sujette aux erreurs.
Darryl le
1
Au moins, il est assez facile de voir que "enum foo {a = 10, LAST}" va être étrange. Et je pensais que "int arr [LAST]" serait 11 éléments dans ce cas, pas 2, donc la plupart du code fonctionnera (mais vous gaspillez de la mémoire sur des valeurs d'index invalides)
Code Abominator
32

Pour C ++, il existe diverses techniques d'énumération de type sécurisé disponibles, et certaines d'entre elles (comme le Boost.Enum proposé mais jamais soumis ) incluent le support pour obtenir la taille d'une énumération.

L'approche la plus simple, qui fonctionne aussi bien en C qu'en C ++, consiste à adopter une convention de déclaration d'une valeur ... MAX pour chacun de vos types enum:

enum Folders { FA, FB, FC, Folders_MAX = FC };
ContainerClass *m_containers[Folders_MAX + 1];
....
m_containers[FA] = ...; // etc.

Edit : Concernant { FA, FB, FC, Folders_MAX = FC}versus {FA, FB, FC, Folders_MAX]: je préfère définir la valeur ... MAX sur la dernière valeur légale de l'énumération pour plusieurs raisons:

  1. Le nom de la constante est techniquement plus précis (car Folders_MAXdonne la valeur d'énumération maximale possible).
  2. Personnellement, j'ai l'impression Folders_MAX = FC se démarquer un peu plus des autres entrées (ce qui rend un peu plus difficile d'ajouter accidentellement des valeurs d'énumération sans mettre à jour la valeur maximale, un problème auquel Martin York a fait référence).
  3. GCC inclut des avertissements utiles comme "valeur d'énumération non incluse dans le commutateur" pour du code tel que le suivant. Laisser Folders_MAX == FC + 1 annule ces avertissements, car vous vous retrouvez avec un tas de valeurs d'énumération ... MAX qui ne devraient jamais être incluses dans le commutateur.
commutateur (dossier) 
{
  cas FA: ...;
  cas FB: ...;
  // Oups, j'ai oublié FC!
}
Josh Kelley
la source
3
Pourquoi ne pas faire enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];:?
Bill
1
Je préfère préciser que le dernier est un nombre, et ils portent tous le même nom grâce à:struct SomeEnum { enum type {FA, FB, FC, NB__};};
Luc Hermitte
2
En fait, je pense que ces avertissements "utiles" sont une vraie douleur dans le cul. J'aime les bons avertissements que j'ai toujours mis -Wall -pedantic, etc. lorsque je développe, mais ces avertissements sont tout simplement stupides. Il n'y a que quelques pires, comme suggérer des parens pour && || et & ^ | priorité des opérateurs. Je veux dire, je pensais que java était le langage de la baby-sitter, qu'est-ce qui se passe en C et C ++ ...
qui le
2
L'inconvénient d'avoir Folders_max = FC est que vous devez le changer à chaque fois que vous ajoutez quelque chose à l'énumération!
Étienne
2
enum Dossiers {FA, FB, FC, Folders_MIN = FA, Folders_MAX = FC}; Juste pour souligner qu'il est utile pour l'itération?
gjpc
7

Qu'en est-il des traits, à la mode STL? Par exemple:

enum Foo
{
    Bar,
    Baz
};

écrire un

std::numeric_limits<enum Foo>::max()

spécialisation (éventuellement constexpr si vous utilisez c ++ 11). Ensuite, dans votre code de test, fournissez toutes les assertions statiques pour maintenir les contraintes que std :: numeric_limits :: max () = last_item.

Wojciech Migda
la source
2
Malheureusement, cela ne fonctionnera pas selon cette réponse .
rr-
3
std::numeric_limits<enum Foo>::max()retourne toujours zéro ... (voir la question pour la réponse liée) Testé sur les enums normaux ( enum Foo { ... }), les enums à indice de type ( enum class Foo : uint8_t { ... }) avec gcc 5.2.0 @ Linux et MinGW 4.9.3 @ Windows.
rr-
1
(... et dans le cas de std::numeric_limits<std::underlying_type<Foo>::type>::max(), il renvoie la valeur maximale du type sous-jacent, c'est-à-dire 0xFFFFFFFF pour les entiers de 32 bits, ce qui n'est pas utile dans ce contexte.)
rr-
1
Etrange, car j'ai un code fonctionnel qui fait ce que j'ai décrit. namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } Puis: namespace std { template<> struct numeric_limits<enum ::gx::DnaNucleobase> { typedef enum ::gx::DnaNucleobase value_type; static constexpr value_type max() { return value_type::T; } (...) Et std::cout << std::numeric_limits<::gx::DnaNucleobase>::max() << std::endl;imprime le résultat attendu. Testé avec les saveurs gcc 5.2.1 et 4.8 / 4.9.
Wojciech Migda
2
-1; envoyez-moi un ping si vous modifiez la réponse, afin que je puisse annuler le vote négatif. Cette réponse est une mauvaise convention à suivre. C'est une mauvaise utilisation du concept de numeric_limits<T>::max(). La seule chose que la fonction peut raisonnablement renvoyer est la valeur énumérée la plus élevée. Il reviendrait 2, mais l'OP (dans ce cas précis) en aurait besoin pour revenir 3. Une fois que vous avez des valeurs non par défaut pour l'énumération ( FB = 2057), tous les paris sont désactivés, vous ne pouvez même + 1pas pirater l'erreur off-by-one. S'il y avait un numeric_limits<T>::number_of_elements_of_the_set()(ou un nom plus court), cela pourrait être utilisé sans ambiguïté.
Merlyn Morgan-Graham
3

Ajoutez une entrée, à la fin de votre énumération, appelée Folders_MAX ou quelque chose de similaire et utilisez cette valeur lors de l'initialisation de vos tableaux.

ContainerClass* m_containers[Folders_MAX];
Kevin Doyon
la source
2

J'aime utiliser les énumérations comme arguments pour mes fonctions. C'est un moyen simple de fournir une liste fixe d '"options". Le problème avec la réponse la plus votée ici est qu'en utilisant cela, un client peut spécifier une "option invalide". En tant que spin-off, je recommande de faire essentiellement la même chose, mais d'utiliser une constante int en dehors de l'énumération pour en définir le nombre.

enum foobar { foo, bar, baz, quz };
const int FOOBAR_NR_ITEMS=4;

Ce n'est pas agréable, mais c'est une solution propre si vous ne modifiez pas l'énumération sans mettre à jour la constante.

BuvinJ
la source
1

Je ne vois vraiment aucun moyen d'obtenir vraiment le nombre de valeurs dans une énumération en C ++. Toute solution de mention précédente fonctionne tant que vous ne définissez pas la valeur de vos énumérations si vous définissez une valeur que vous pourriez rencontrer dans des situations où vous créez des tableaux trop grands ou trop petits

enum example{ test1 = -2, test2 = -1, test3 = 0, test4 = 1, test5 = 2 }

dans cet exemple, le résultat créerait un tableau de 3 éléments lorsque vous avez besoin d'un tableau de 5 éléments

enum example2{ test1 , test2 , test3 , test4 , test5 = 301 }

dans cet exemple, le résultat créerait un tableau de 301 éléments lorsque vous avez besoin d'un tableau de 5 éléments

La meilleure façon de résoudre ce problème dans le cas général serait de parcourir vos énumérations, mais ce n'est pas encore dans la norme pour autant que je sache


la source