L'itération sur les non-incrémentale Enum
Avant de vous demander, j'ai regardé et regardé pour cette SORTE, et ne peut pas trouver une réponse solide.
J'ai besoin d'être en mesure de dynamiquement itérer sur un enum qui a des non-valeurs incrémentielles, à titre d'exemple:
typedef enum {
CAPI_SUBTYPE_NULL = 0, /* Null subtype. */
CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */
CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */
CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */
CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */
CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */
CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */
CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */
CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */
CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */
CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */
CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */
CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */
CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */
CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */
CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */
CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */
CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */
CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */
CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */
CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */
CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */
CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */
CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */
CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */
CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */
CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */
CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */
CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */
CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */
CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */
CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */
CAPI_SUBTYPE_DIAG_ASG = 59 /* ASG diagram. */
} CAPI_SUBTYPE_E ;
La raison pour laquelle je voudrais pouvoir faire, c'est parce que l'enum est donné dans une API (qui je ne peux pas changer, évidemment) et que vous préférez être en mesure, quelle que soit la version de l'API, être en mesure d'effectuer une itération sur ces valeurs.
N'importe quelle direction est apprécié.
- C++ ne fournit pas de support direct pour itérer sur les énumérations. Farcir dans un
std::vector
oustd::array
. - Je déteste quand les bibliothèques utilisent des énumérations de ce genre. Le vecteur des recommandations sont solides.
- En plus de mon commentaire précédent (et merci à iammilind pour me motiver dans une autre direction), je voudrais suggérer à l'aide de
initializer_list
survector
ouarray
puisque c'est la garantie d'être un littéral. - voir la accepté de répondre à stackoverflow.com/questions/15451382/...
Vous devez vous connecter pour publier un commentaire.
Avec le C++, le seul moyen pour itérer sur les enums est de les stocker dans un tableau et parcourir la même. Le principal défi est de savoir comment suivre le même ordre dans le
enum
déclaration et la déclaration de tableau?Vous pouvez automatiser la façon dont vous les commander dans la
enum
ainsi que tableau. J'ai l'impression que c'est une manière décente:Maintenant, vous
#include
ce fichier dans votre déclaration d'enum et de déclaration de tableau à la fois des lieux avec macro redéfinition:Et de mettre le même fichier pour le tableau avec d'autres macro définition:
Maintenant effectuer une itération en C++03:
ou encore simple en C++11:
Il est à propos de délicat et de plus C qu'en C++ de la pratique, mais vous pouvez utiliser X macros. Il est très laid et que vous devez garder la TABLE dans le bon ordre. En C++, je crois que nous n'avons pas besoin d'itérer sur les énumérations et de plus nous n'avons pas besoin d'assigner des valeurs de l'énumération (soi-disant valeur de l'énumération est aléatoire dans chaque compilation). Alors, pensez à cela comme une blague 🙂
for(int i = 0; ENUM_FIRST; i < ENUM_LAST; ++i) do_smth(data[i])
. Aussi je n'aime pas quand les valeurs de l'énumération sont affectés par les programmeurs, parce que la norme ne nous empêche pas de doublons. Donc par "les valeurs sont aléatoires" je veux dire "en dépit du fait que la valeur par défaut deenum
est garanti par la norme, je préfère considérer que les valeurs de l'enum sont randomisés".Je suis d'accord avec le déjà donné des déclarations que ce n'est pas possible sans que ce soit de modifier ou de copier les définitions de la
enum
. Toutefois, en C++11 (peut-être même en C++03?) vous pouvez aller aussi loin que de fournir une syntaxe où tout ce que vous avez à faire (littéralement) est de copier et coller l'agent recenseur définitions de laenum
dans une macro. Cela fonctionne tant que chaque agent recenseur a une définition explicite (à l'aide de=
).Edit: Vous pouvez étendre ce travail, même si pas chaque agent recenseur a une définition explicite, mais cela ne devrait pas être nécessaire dans ce cas.
J'ai une fois développé pour certains physiciens, afin que l'exemple est sur les particules.
Exemple d'utilisation:
La macro rendements (efficace):
définition de macro
Remarque: le type
iterable_enum_
pourrait ainsi être défini une fois à l'extérieur de la macro.macro explication
L'idée est de permettre à une syntaxe comme
proton = 11, electron = 12
au sein de la macro invocation. Cela fonctionne très facile pour n'importe quel type de déclaration, mais il crée des problèmes pour stocker les noms:rendements:
Comme avec beaucoup de syntaxe astuces, la surcharge d'opérateur fournit un moyen de surmonter ce problème; mais l'opérateur d'affectation doit être un membre de fonctions et les énumérations ne sont pas des classes.
Alors pourquoi ne pas utiliser certains objets constants au lieu d'un enum?
Ce n'est pas encore résoudre notre problème, il démontre simplement que nous pouvons facilement remplacer les énumérations, par une liste de constantes si nous n'avons pas besoin de l'auto-incrément de fonction des agents recenseurs.
Maintenant, la syntaxe truc avec la surcharge d'opérateur:
Le truc, c'est, dans la ligne (1) le
=
désigne une copie de l'initialisation, qui est fait en convertissant le nombre (11
,22
) pour temporaire de typeparticle
par l'aide de la (ctor) et la copie/le déplacement temporaire par le biais d'un implicitement défini ctor à l'objet de destination (proton
,electron
).En revanche, la
=
en ligne (2) est résolu à un opérateur d'appel de (op), qui renvoie une copie de l'objet sur lequel il a été appelé (*this
). Leconstexpr
stuff permet d'utiliser ces variables au moment de la compilation, par exemple, dans un modèle de déclaration. En raison de restrictions surconstexpr
fonctions, nous ne pouvons tout simplement pas de retour*this
dans l' (op) de la fonction. En outre,constexpr
implique toutes les restrictions deconst
.En fournissant une conversion implicite de l'opérateur, vous pouvez créer le tableau en ligne (2) de type
ParticleEnum
:operator=
truc dans ma réponse (variadic les macros ne sont pas en C++03, tout de même). Leoperator=
estconstexpr
parce que j'ai déjà utilisé la non-type de modèle arguments au lieu d'un type de conteneur (std::initializer_list
) afin de collecter les agents recenseurs, mais a eu des problèmes avec différents compilateurs (affectation est explicitement interdit dans les expressions pour les non-type de modèle arguments).Fondée sur les articles donnés au début de la question, j'ai tiré une solution qui est basée sur l'hypothèse que vous connaissez les invalides plages.
J'ai vraiment envie d'sait si c'est une bonne solution.
D'abord, à la fin vous enum avec quelque chose comme ça:
CAPI_END = 60
. Elle contribue à interates. Donc mon code est:Je suis fournissant seulement un pré opérateur d'incrémentation. Un poste opérateur d'incrémentation est de laisser à implemanted plus tard.
La réponse est "non, vous ne pouvez pas effectuer une itération sur les éléments d'un
enum
en C++03 ou C++11".Maintenant, vous pouvez décrire l'ensemble des valeurs d'un
enum
dans une manière qui peut être comprise au moment de la compilation.qui vous donne un type
CAPI_SUBTYPE_E_LIST
qui encapsule la liste desenum
valeurs.On peut alors remplir un tableau avec ces facilement:
si vous en avez vraiment besoin. Mais ce n'est qu'un cas particulier du cas plus général d'être capable de générer du code pour chaque élément de votre
enum CAPI_SUBTYPE_E
-- directement la construction d'unfor
boucle n'est pas nécessaire.De manière amusante, avec un compatible C++11 compilateur, nous pourrions écrire du code qui permettrait de générer notre
CAPI_SUBTYPE_E_LIST
avec notammentenum
éléments iff ces éléments sont réellement dansCAPI_SUBTYPE_E
à l'aide de SFINAE. Ce serait utile parce qu'on peut prendre la version la plus récente de l'API, nous pouvons soutenir, et faire l'auto-dégrader (au moment de la compilation) si l'API, nous compilons contre est plus primitif.Pour démontrer la technique, je vais commencer avec un jouet
enum
Imaginer que
B=1
est retirée dans la version plus moderne de l'API, mais il n'est pas dans la plus primitive.maintenant, si j'ai écrit ce droit,
TypedList<CAPI_SUBTYPE_E>
contientB
iffB
est défini comme un élément deCAPI_SUBTYPE_E
. Cela vous permet de compiler à l'encontre de plus d'une version de la bibliothèque, et obtenir un autre ensemble d'éléments dans votreenum
élément de la liste en fonction de ce qui est dans la bibliothèque. Vous n'avez pas à maintenir ce gênant passe-partout (ce qui pourrait probablement être plus facile avec des macros ou de génération de code) à l'encontre de la "dernière" version de laenum
s éléments, mais il doit gérer automatiquement les versions précédentes au moment de la compilation.Ce tristement nécessite beaucoup d'entretien pour fonctionner.
Enfin, votre exigence que cette dynamique: le seul moyen pratique de cette dynamique est d'envelopper la 3ème partie de l'API dans le code qui sait ce que la version de l'API est, et expose un autre tampon de
enum
valeurs (je l'avais mis dans unstd::vector
) en fonction de ce que la version de l'API. Puis, quand vous chargez de l'API, vous charge de cette aide wrapper, qui utilise ensuite les techniques ci-dessus pour construire l'ensemble des éléments de laenum
, qui vous parcourez.Certains de ce standard peut être plus facile d'écrire avec une horrible macros, comme ceux qui construisent les différentes
AddElementN
type SFINAE code à l'aide de la__LINE__
à l'index les types récursifs. Mais que serait horrible.Un peu plus clair (???) avec un peu de boost de prétraitement.
Vous définissez vos énumérations par une séquence
ensuite, vous pouvez automatiser (par le biais de macros) la déclaration de l'enum,
la table d'index, il
le nombre d'énumérations ou de la taille de la table
et l'accès à l':
Voici le texte intégral.
Vous ne pouvez pas effectuer une itération sur l'arbitraire
enum
en C++. Pour une itération, les valeurs devraient être mis en conteneur. Vous pouvez automatiser le maintien d'un tel conteneur à l'aide de 'énumérer les classes" comme décrit ici: http://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955http://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955Utilisent des macros
Voici la technique que nous utilisons dans nos projets.
Concept:
L'idée est de générer une macro appelée LISTE qui contient la définition des paires nom-valeur, et il en prend une autre macro comme un argument. Dans l'exemple ci-dessous j'ai défini deux les macros d'assistance. 'GENERATE_ENUM' pour générer l'enum et "GENERATE_ARRAY' pour générer un iteratable tableau. Bien sûr, cela peut être prolongée si nécessaire. Je pense que cette solution vous donne la plupart de coup pour le mâle.
En théorie, elle est très similaire à iammilind de la solution.
Exemple:
Utilisation:
Maintenant dans le code, vous pouvez vous référer à la enum comme ceci:
Vous pouvez effectuer une itération sur l'énumération comme ceci:
Remarque:
Si je me souviens bien, C++11 enums vivre dans leurs propres espaces de noms (comme en Java ou C#) , dans ce cas, cette utilisation ne fonctionnerait pas. Vous devez vous référer à l'énumération des valeurs de ce type CAPI_SUBTYPES::Enum::FooBar.
les prémices d'une solution impliquant pas de macros et (presque) pas de gestion d'exécution:
de sortie:
Les mettre dans un tableau ou un autre récipient et parcourir plus de cela. Si vous modifiez l'enum, vous devrez mettre à jour le code, ce qui les place dans le conteneur.
Depuis un enum ne permet pas d'itération, vous devez créer une alternative à la représentation de l'enum et de sa gamme de valeurs.
L'approche que je prendrais serait une simple lecture de la table intégré dans une classe. Le problème est que comme l'API modifie son enum avec de nouvelles entrées, vous aussi, vous avez besoin de mettre à jour le constructeur de cette classe.
La classe simple que je voudrais utiliser devrait être composée d'un constructeur pour construire la table avec un couple de méthodes pour effectuer une itération sur la table. Puisque vous aussi vous voulez savoir si il y a un problème avec la taille de la table lors de l'ajout d'éléments, vous pouvez utiliser un
assert ()
macro qui n'assert()
en mode de débogage. Dans la source de l'exemple ci-dessous, j'utilise le préprocesseur pour tester la compilation de débogage ou non et s'affirmer a été inclus ou non, de façon à fournir un mécanisme de base pour la vérification de la cohérence.J'ai emprunté une idée que j'ai vu à partir de la P. J. Plauger dans son livre de la Bibliothèque Standard C de l'aide d'un simple tableau de recherche de caractères ANSI opérations dont le caractère est utilisé pour les index dans une table.
L'utilisation de cette classe que vous feriez quelque chose comme le suivant, qui utilise un
for
boucle d'itération sur les valeurs dans le tableau. Dans le corps de la boucle de vous faire ce que vous voulez faire avec les valeurs enum.Étant donné que cette classe énumère sur les valeurs, j'ai arbitrairement choisi de retourner la valeur de
CAPI_SUBTYPE_NULL
dans les cas où nous avons atteint la fin de l'énumération. De sorte que la valeur de retour dans le cas d'une table de recherche de l'erreur est dans la plage valide toutefois, il ne peut pas être dépendait. Il y a donc une case sur laEnd()
méthode qui devrait être fait pour voir si la fin d'une itération a été atteint. Aussi après avoir fait de la construction de l'objet on peut vérifier lesm_bTableError
membre de données pour voir si il y avait une erreur lors de la construction.La source d'exemple pour la classe suivante. Vous devez mettre à jour le constructeur avec les valeurs enum pour l'API, comme ils changent. Malheureusement, il n'ya pas beaucoup qui peut être fait pour automatiser le contrôle sur une mise à jour enum, cependant nous disposons de tests en place dans une compilation de débogage pour vérifier que la table est assez grande et que la valeur de l'enum de la mettre dans le tableau est dans la gamme de la taille de la table.
Et si vous le souhaitez, vous pouvez ajouter une autre méthode pour récupérer la valeur actuelle de l'itération. Avis que, plutôt que d'incrémentation à l'autre, le Courant() méthode utilise quelle que soit l'itération de l'indice est actuellement à et commence à chercher à partir de la position actuelle. Donc, si la position actuelle est une valeur valide il retourne juste ça sinon il va trouver la première valeur valide. Alternativement, vous pouvez faire ce juste retour de l'actuel tableau de la valeur pointé par l'indice et si la valeur est incorrecte, définir un indicateur d'erreur.
Ici une autre approche. Un bonus est que votre compilateur peut vous avertir si vous omettez une valeur d'enum dans un
switch
:default:
cas, surtout qu'elle ne fait rien.-Wswitch-enum
est précisément l'avertissement clang émet sous mon avertissement standard set - indépendamment de la présence ou de l'absence dedefault
. GCC (qui clang a été conçu pour être utile comme dropin substitut) est susceptible de fonctionner très similaire lorsque que l'avertissement est activé.Je suis en utilisant ce type de constructions à définir mon propre énumérations:
Vous pouvez aussi remplacer
storage_
type deboost::bimap
avoir bidirectionnel correspondance int <==> stringIl y a beaucoup de réponses à cette question déjà, mais la plupart d'entre eux sont soit très compliqué et inefficace en ce qu'ils ne sont pas directement l'adresse de l'exigence d'une itération sur un enum avec des lacunes. Tout le monde jusqu'à présent, a dit que ce n'est pas possible, et ils sont en quelque sorte de les corriger, il n'existe pas de fonctionnalité pour vous permettre de le faire. Qui certainement ne signifie pas que vous ne pouvez pas, et comme on peut le voir par toutes les réponses jusqu'à présent, il existe de nombreuses façons de le faire. Voici ma voie, basée sur l'énumération que vous avez fournies et l'hypothèse que c'est la structure ne change pas beaucoup. Bien sûr, cette méthode peut être adaptée au besoin.
Ou quelque chose le long de ces lignes.
La seule vraie "solution" je suis enfin arrivé à résoudre ce problème est de créer un pré-exécuter un script qui lit le c/c++ fichier(s) contenant les enums et génère un fichier de classe qui a une liste de tous les enums en tant que vecteurs. C'est beaucoup de la même manière Visual Studio prend en charge Les Modèles De T4. Dans l' .Monde Net c'est assez pratique courante, mais depuis je ne peux pas travailler dans cet environnement, j'ai été forcé de le faire de cette façon.
Le script que j'ai écrit est en Ruby, mais vous pourriez le faire dans quelque langue que ce soit. Si quelqu'un veut le script d'origine, je l'ai transféré ici. Ce n'est pas un script parfait, mais il fit le projet de loi pour mon projet. J'encourage tout le monde à s'améliorer et donner des conseils ici.