Traiter les enum
s comme des indicateurs fonctionne bien en C # via l' [Flags]
attribut, mais quelle est la meilleure façon de le faire en C ++?
Par exemple, j'aimerais écrire:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
seahawk.flags = CanFly | EatsFish | Endangered;
Cependant, j'obtiens des erreurs de compilation concernant int
/ enum
conversions. Existe-t-il une meilleure façon d'exprimer cela qu'un simple casting brutal? De préférence, je ne veux pas me fier à des constructions de bibliothèques tierces telles que boost ou Qt.
EDIT: Comme indiqué dans les réponses, je peux éviter l'erreur du compilateur en déclarant seahawk.flags
comme int
. Cependant, j'aimerais avoir un mécanisme pour appliquer la sécurité de type, afin que quelqu'un ne puisse pas écrire seahawk.flags = HasMaximizeButton
.
[Flags]
attribut fonctionne très bien, c'est-à-dire:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};
Réponses:
La manière "correcte" est de définir des opérateurs de bits pour l'énumération, comme:
Etc. reste des opérateurs de bits. Modifiez si nécessaire si la plage d'énumération dépasse la plage int.
la source
AnimalFlags
est représenté par l'expressionHasClaws | CanFly
? Ce n'est pas à cela queenum
servent les s. Utilisez des entiers et des constantes.(HasClaws | CanFly)
".HasClaws
(= 1) etCanFly
(= 2). Si, à la place, vous attribuez simplement les valeurs 1 à 4 et que vous obtenez un 3, il peut s'agir d'un simpleEatsFish
ou encore d'une combinaison deHasClaws
etCanFly
. Si votre énumération ne dénote que des états exclusifs, les valeurs consécutives conviennent, mais une combinaison d'indicateurs nécessite que les valeurs soient exclusives au bit.Remarque (également un peu hors sujet): Une autre façon de créer des indicateurs uniques peut être effectuée en utilisant un décalage de bits. Moi-même, je trouve cela plus facile à lire.
Il peut contenir des valeurs jusqu'à un int, c'est-à-dire, la plupart du temps, 32 indicateurs, ce qui est clairement reflété dans la quantité de décalage.
la source
Pour les paresseux comme moi, voici une solution basée sur un modèle pour copier et coller:
la source
using
le cas échéant, commerel_ops
.Notez que si vous travaillez dans un environnement Windows, il existe une
DEFINE_ENUM_FLAG_OPERATORS
macro définie dans winnt.h qui fait le travail pour vous. Donc dans ce cas, vous pouvez faire ceci:la source
Quel type est la variable seahawk.flags?
Dans C ++ standard, les énumérations ne sont pas de type sécurisé. Ce sont en fait des nombres entiers.
AnimalFlags ne doit PAS être le type de votre variable. Votre variable doit être int et l'erreur disparaîtra.
Il n'est pas nécessaire de mettre des valeurs hexadécimales comme certaines personnes l'ont suggéré. Ça ne fait aucune différence.
Les valeurs d'énumération SONT de type int par défaut. Vous pouvez donc sûrement les combiner au niveau du bit OU les combiner et les assembler et stocker le résultat dans un int.
Le type enum est un sous-ensemble restreint de int dont la valeur est l'une de ses valeurs énumérées. Par conséquent, lorsque vous créez une nouvelle valeur en dehors de cette plage, vous ne pouvez pas l'affecter sans transtyper en une variable de votre type enum.
Vous pouvez également modifier les types de valeur d'énumération si vous le souhaitez, mais cette question ne sert à rien.
ÉDITER: L'affiche a déclaré qu'ils étaient préoccupés par la sécurité des types et qu'ils ne veulent pas d'une valeur qui ne devrait pas exister à l'intérieur du type int.
Mais il serait de type unsafe de placer une valeur en dehors de la plage d'AnimalFlags dans une variable de type AnimalFlags.
Il existe un moyen sûr de vérifier les valeurs hors plage à l'intérieur du type int ...
Ce qui précède ne vous empêche pas de mettre un indicateur non valide d'une énumération différente qui a la valeur 1, 2, 4 ou 8.
Si vous voulez une sécurité de type absolue, vous pouvez simplement créer un std :: set et stocker chaque drapeau à l'intérieur. Ce n'est pas peu encombrant, mais il est sûr de type et vous donne la même capacité qu'un bitflag int.
Remarque C ++ 0x: énumérations fortement typées
En C ++ 0x, vous pouvez enfin avoir des valeurs d'énumération de type safe ...
la source
HasClaws | CanFly
est un type entier, mais le type deHasClaws
estAnimalFlags
, pas un type entier.max(|emin| − K, |emax|)
et égale à(1u<<M) - 1
, oùM
est un entier non négatif. "int
.enum
n'est pas techniquement par défautint
son type sous-jacent (soit avant C ++ 11 (IIRC), soit post-C ++ 11 lorsqu'aucun type sous-jacent n'est spécifié), bien que ce soit leenum class
cas . Au lieu de cela, le type sous-jacent prend par défaut quelque chose d'assez grand pour représenter tous les énumérateurs, avec la seule vraie règle dure qu'il est seulement plus grand queint
s'il doit explicitement l' être. Fondamentalement, le type sous-jacent est spécifié comme (paraphrasé) "tout ce qui fonctionne, mais c'est probablement àint
moins que les énumérateurs ne soient trop gros pourint
".Je trouve la réponse actuellement acceptée par eidolon trop dangereuse. L'optimiseur du compilateur peut émettre des hypothèses sur les valeurs possibles de l'énumération et vous pouvez récupérer des déchets avec des valeurs non valides. Et généralement, personne ne veut définir toutes les permutations possibles dans les énumérations d'indicateurs.
Comme Brian R. Bondy le déclare ci-dessous, si vous utilisez C ++ 11 (ce que tout le monde devrait faire, c'est aussi bien), vous pouvez maintenant le faire plus facilement avec
enum class
:Cela garantit une taille et une plage de valeurs stables en spécifiant un type pour l'énumération, empêche le downcasting automatique des énumérations en ints, etc. en utilisant
enum class
, et utiliseconstexpr
pour garantir que le code des opérateurs soit incorporé et donc aussi rapide que les nombres réguliers.Pour les personnes bloquées avec des dialectes C ++ pré-11
Si j'étais coincé avec un compilateur qui ne prend pas en charge C ++ 11, j'irais avec l'encapsulation d'un type int dans une classe qui autorise alors uniquement l'utilisation d'opérateurs au niveau du bit et les types de cette énumération pour définir ses valeurs:
Vous pouvez définir cela à peu près comme un enum + typedef normal:
Et l'utilisation est également similaire:
Et vous pouvez également remplacer le type sous-jacent pour les énumérations binaires stables (comme C ++ 11
enum foo : type
) en utilisant le deuxième paramètre de modèle, ietypedef SafeEnum<enum TFlags_,uint8_t> TFlags;
.J'ai marqué le
operator bool
remplacement avec leexplicit
mot - clé de C ++ 11 pour l'empêcher de générer des conversions int, car ceux-ci pourraient entraîner la réduction des ensembles d'indicateurs en 0 ou 1 lors de leur écriture. Si vous ne pouvez pas utiliser C ++ 11, laissez cette surcharge et réécrivez le premier conditionnel dans l'exemple d'utilisation comme(myFlags & EFlagTwo) == EFlagTwo
.la source
std::underlying_type
au lieu de coder en dur un type spécifique, ou que le type sous-jacent soit fourni et utilisé comme alias de type au lieu de directement. De cette façon, les modifications du type sous-jacent se propageront automatiquement, au lieu de devoir être effectuées manuellement.Le moyen le plus simple de le faire, comme indiqué ici , en utilisant le jeu de bits de la classe de bibliothèque standard .
Pour émuler la fonctionnalité C # d'une manière sûre pour le type, vous devez écrire un wrapper de modèle autour du jeu de bits, en remplaçant les arguments int par une énumération donnée en tant que paramètre de type au modèle. Quelque chose comme:
la source
À mon avis, aucune des réponses à ce jour n'est idéale. Pour être idéal, j'attendrais la solution:
==
,!=
,=
,&
,&=
,|
,|=
et les~
opérateurs dans le sens conventionnel ( par exemplea & b
)if (a & b)...
La plupart des solutions à ce jour tombent sur les points 2 ou 3. WebDancer est la clôture à mon avis mais échoue au point 3 et doit être répétée pour chaque énumération.
Ma solution proposée est une version généralisée de WebDancer's qui aborde également le point 3:
Cela crée des surcharges des opérateurs nécessaires mais utilise SFINAE pour les limiter aux types énumérés. Notez que dans un souci de concision, je n'ai pas défini tous les opérateurs mais le seul qui soit différent est le
&
. Les opérateurs sont actuellement globaux (c'est-à-dire s'appliquent à tous les types énumérés) mais cela pourrait être réduit soit en plaçant les surcharges dans un espace de noms (ce que je fais), soit en ajoutant des conditions SFINAE supplémentaires (peut-être en utilisant des types sous-jacents particuliers, ou des alias de type spécialement créés ). leunderlying_type_t
s'agit d'une fonctionnalité C ++ 14 mais elle semble bien prise en charge et est facile à émuler pour C ++ 11 avec un simpletemplate<typename T> using underlying_type_t = underlying_type<T>::type;
la source
Le standard C ++ en parle explicitement, voir la section "17.5.2.1.3 Types de masque de bits":
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf
Compte tenu de ce "modèle", vous obtenez:
Et pareil pour les autres opérateurs. Notez également le "constexpr", il est nécessaire si vous voulez que le compilateur puisse exécuter les opérateurs lors de la compilation.
Si vous utilisez C ++ / CLI et que vous souhaitez attribuer des membres enum aux classes ref, vous devez utiliser des références de suivi à la place:
REMARQUE: cet exemple n'est pas complet, voir la section "17.5.2.1.3 Types de masque de bits" pour un ensemble complet d'opérateurs.
la source
Je me suis retrouvé à poser la même question et j'ai proposé une solution générique basée sur C ++ 11, similaire à celle de soru:
L'interface peut être améliorée au goût. Ensuite, il peut être utilisé comme ceci:
la source
Si votre compilateur ne prend pas encore en charge les énumérations fortement typées, vous pouvez consulter l' article suivant à partir de la source c ++:
Du résumé:
la source
J'utilise la macro suivante:
Il est similaire à ceux mentionnés ci-dessus mais présente plusieurs améliorations:
int
)Il doit inclure type_traits:
la source
Je voudrais développer la réponse d'Uliwitness , en corrigeant son code pour C ++ 98 et en utilisant l' idiome Safe Bool , faute de
std::underlying_type<>
modèle et deexplicit
mot clé dans les versions C ++ inférieures à C ++ 11.Je l'ai également modifié pour que les valeurs d'énumération puissent être séquentielles sans aucune affectation explicite, afin que vous puissiez avoir
Vous pouvez ensuite obtenir la valeur des indicateurs bruts avec
Voici le code.
la source
Voici une option pour les masques de bits si vous n'avez pas réellement d'utilisation des valeurs d'énumération individuelles (par exemple, vous n'avez pas besoin de les désactiver) ... et si vous n'êtes pas préoccupé par le maintien de la compatibilité binaire, c'est-à-dire: vous peu importe où vivent vos morceaux ... ce que vous êtes probablement. De plus, vous feriez mieux de ne pas vous préoccuper trop de la portée et du contrôle d'accès. Hmmm, les enums ont de belles propriétés pour les champs de bits ... je me demande si quelqu'un a déjà essayé ça :)
Nous pouvons voir que la vie est belle, nous avons nos valeurs discrètes, et nous avons un bel intérêt pour & et | à notre coeur content, qui a toujours le contexte de ce que signifient ses morceaux. Tout est cohérent et prévisible ... pour moi ... tant que je continue à utiliser le compilateur VC ++ de Microsoft avec la mise à jour 3 sur Win10 x64 et que je ne touche pas les drapeaux de mon compilateur :)
Même si tout va bien ... nous avons maintenant un certain contexte quant à la signification des drapeaux, car c'est dans une union avec le champ de bits dans le terrible monde réel où votre programme peut être responsable de plus d'une seule tâche discrète que vous pourriez écrasez toujours accidentellement (assez facilement) deux champs de drapeaux de différents syndicats ensemble (par exemple, AnimalProperties et ObjectProperties, car ils sont tous les deux intimes), mélangeant tous vos bits, ce qui est un horrible bug à retracer ... et comment je sais beaucoup de personnes sur ce post ne travaillent pas très souvent avec des masques bitmap, car leur construction est facile et leur maintenance est difficile.
Donc, vous rendez votre déclaration d'union privée pour empêcher l'accès direct à "Flags", et devez ajouter des getters / setters et des surcharges d'opérateurs, puis créer une macro pour tout cela, et vous êtes fondamentalement de retour là où vous avez commencé lorsque vous avez essayé de faites cela avec un Enum.
Malheureusement, si vous voulez que votre code soit portable, je ne pense pas qu'il y ait moyen de A) garantir la disposition des bits ou B) déterminer la disposition des bits au moment de la compilation (afin que vous puissiez le suivre et au moins corriger les changements à travers versions / plates-formes, etc.) Offset dans une structure avec des champs de bits
Au moment de l'exécution, vous pouvez jouer des tours en définissant les champs et en XORing les indicateurs pour voir quels bits ont changé, cela me semble assez merdique si les versets ont une solution 100% cohérente, indépendante de la plate-forme et complètement déterministe, c'est-à-dire: un ENUM.
TL; DR: N'écoutez pas les haineux. C ++ n'est pas l'anglais. Ce n'est pas parce que la définition littérale d'un mot-clé abrégé hérité de C peut ne pas correspondre à votre utilisation que vous ne devriez pas l'utiliser lorsque le C et définition C ++ du mot clé inclut absolument votre cas d'utilisation. Vous pouvez également utiliser des structures pour modéliser des choses autres que des structures et des classes pour des choses autres que l'école et la caste sociale. Vous pouvez utiliser float pour les valeurs mises à la terre. Vous pouvez utiliser char pour les variables qui ne sont ni non brûlées ni une personne dans un roman, une pièce de théâtre ou un film. Tout programmeur qui va au dictionnaire pour déterminer la signification d'un mot-clé avant la spécification de la langue est un ... eh bien, je vais garder ma langue là-bas.
Si vous voulez que votre code soit modelé sur le langage parlé, vous feriez mieux d'écrire en Objective-C, qui utilise également fortement les énumérations pour les champs de bits.
la source
Uniquement du sucre syntaxique. Aucune métadonnée supplémentaire.
Les opérateurs de drapeau sur le type intégral fonctionnent simplement.
la source
Actuellement, il n'y a pas de prise en charge du langage pour les indicateurs enum, les classes Meta pourraient intrinsèquement ajouter cette fonctionnalité si elle faisait jamais partie du standard c ++.
Ma solution serait de créer des fonctions de modèle instanciées enum uniquement en ajoutant la prise en charge des opérations bit à bit de type sécurisé pour la classe enum en utilisant son type sous-jacent:
Fichier: EnumClassBitwise.h
Pour plus de commodité et pour réduire les erreurs, vous voudrez peut-être encapsuler vos opérations d'indicateurs de bits pour les énumérations et pour les entiers également:
Fichier: BitFlags.h
Utilisation possible:
la source
@Xaqq a fourni une très bonne façon d'utiliser les indicateurs enum ici par une
flag_set
classe.J'ai publié le code dans GitHub , l'utilisation est la suivante:
la source
Vous confondez objets et collections d'objets. Plus précisément, vous confondez les indicateurs binaires avec des ensembles d'indicateurs binaires. Une solution appropriée ressemblerait à ceci:
la source
Voici ma solution sans avoir besoin de surcharge ou de casting:
Je pense que c'est correct, car nous identifions de toute façon les énumérations et les entiers (non fortement typés).
Juste comme une note latérale (plus longue), si vous
Je proposerais ceci:
en utilisant les listes d'initialiseurs C ++ 11 et
enum class
.la source
using Flags = unsigned long
intérieur d'un espace de noms ou d'une structure contenant les valeurs d'indicateur elles-mêmes comme/*static*/ const Flags XY = 0x01
et ainsi de suite.Comme ci-dessus (Kai) ou procédez comme suit. Vraiment les énumérations sont des "énumérations", ce que vous voulez faire est d'avoir un ensemble, donc vous devriez vraiment utiliser stl :: set
la source
Peut-être comme NS_OPTIONS d'Objective-C.
la source