2009-10-27 4 views
11

Je ne suis pas très utilisé pour la programmation avec des drapeaux, mais je pense que je viens de trouver une situation où ils seraient utiles:Drapeaux, ENUM (C)

J'ai deux objets qui s'inscrire en tant qu'auditeurs de certains événements. Les événements auxquels ils s'inscrivent dépendent d'une variable qui leur est envoyée lors de leur construction. Je pense qu'une bonne façon de le faire serait d'envoyer des variables OU connectées au bit, comme ceci: TAKES_DAMAGE | GRABBABLE | LIQUID, etc. Ensuite, dans le constructeur, l'objet peut vérifier quels drapeaux sont définis et s'enregistrer comme écouteur pour ceux qui le sont.

Mais c'est là que je suis confus. De préférence, les drapeaux seraient dans une énumération. Mais c'est aussi un problème. Si nous avons ces drapeaux:

enum 
{ 
    TAKES_DAMAGE,/* (0) */ 
    GRABBABLE, /* (1) */ 
    LIQUID, /* (2) */ 
    SOME_OTHER /* (3) */ 
}; 

Puis envoyez le SOME_OTHER drapeau (3) sera le même que l'envoi GRABBABLE | LIQUIDE, n'est-ce pas?

Comment gérez-vous exactement ce genre de choses?

Répondre

45

Votre énumération doit être de deux pouvoirs:

enum 
{ 
    TAKES_DAMAGE = 1, 
    GRABBABLE = 2, 
    LIQUID = 4, 
    SOME_OTHER = 8 
}; 

Ou de façon plus lisible:

enum 
{ 
    TAKES_DAMAGE = 1 << 0, 
    GRABBABLE = 1 << 1, 
    LIQUID = 1 << 2, 
    SOME_OTHER = 1 << 3 
}; 

Pourquoi? Parce que vous voulez être en mesure de combiner des drapeaux sans chevauchement, et aussi être en mesure de le faire:

if(myVar & GRABBABLE) 
{ 
    // grabbable code 
} 

... Ce qui fonctionne si les valeurs de l'énumération ressemblent à ceci:

TAKES_DAMAGE: 00000001 
GRABBABLE: 00000010 
LIQUID:  00000100 
SOME_OTHER: 00001000 

Ainsi, dites que vous avez défini myVar-GRABBABLE | TAKES_DAMAGE, voici comment cela fonctionne lorsque vous devez vérifier l'indicateur GRABBABLE:

myVar:  00000011 
GRABBABLE: 00000010 [AND] 
------------------- 
      00000010 // non-zero => converts to true 

Si vous définissez myVar-LIQUID | SOME_OTHER, l'opération aurait entraîné:

myVar:  00001100 
GRABBABLE: 00000010 [AND] 
------------------- 
      00000000 // zero => converts to false 
+0

@jldupont - Je crois que vous vouliez dire "opérateur shiFt"? 8v) –

+0

@jldupont Je pense qu'il te manque une lettre ... – Greg

+0

Je ne pense pas avoir reçu 5 réponses simultanément. o.o Merci pour l'excellente explication et la solution. – quano

4

Vous ne devez utiliser que les puissances de deux drapeaux, c'est-à-dire que chacune est un bit quel que soit le type de données dans lequel vous stockez cette valeur, et rien ne se chevauche lorsque vous utilisez OU bit à bit.

3

vous avez besoin

enum 
{ 
    TAKES_DAMAGE = 1, 
    GRABBABLE = 2, 
    LIQUID = 4, 
    SOME_OTHER = 8 
}; 
+0

Je pense que vous devez corriger la syntaxe sur celui-ci. (Je sais, la même erreur était dans la question.) –

+0

@fred larson, corrigé :-) – Fredou

4

Tu ne peux pas mettre juste les valeurs dans le ENUM?

enum { 
TAKES_DAMAGE = 1, 
GRABBABLE = 2, 
LIQUID  = 4 
} 

Ensuite, exécutez simplement les bits OU sur eux.

5

Oui. Au lieu de cela, faire vos membres enum puissances de 2:

enum 
{ 
    TAKES_DAMAGE = (1 << 0), 
    GRABBABLE = (1 << 1), 
    LIQUID = (1 << 2), 
    SOME_OTHER = (1 << 3) 
}; 
+0

Pour le downvoter: peut-être que vous aimeriez expliquer ce que vous n'avez pas trouvé utile à propos de cette réponse? –

25

une autre façon de stocker des drapeaux est de ne pas déranger le type sous-jacent du tout. Lorsque vous utilisez une énumération, les valeurs enum sont stockées par défaut dans un entier non signé, ce qui correspond à 32 bits sur un ordinateur commun. cela vous donne avec seulement 32 drapeaux possibles: alors que certainement beaucoup, il y a des cas où ce n'est pas suffisant.

maintenant vous pouvez définir votre drapeau défini ainsi:

typedef struct 
{ 
    int takes_damage : 1; 
    int grabbable : 1; 
    int liquid  : 1; 
    int some_other : 1; 
} flags; 

si vous jamais rencontré cela, le « 1 » partie indique au compilateur d'utiliser seulement 1 bit pour stocker ce membre struct.

vous pouvez maintenant définir une variable pour les drapeaux, et de travailler avec ces drapeaux:

flags myflags = {1,0,0,1}; // defines a variable holding a set of flags, with an initial value of takes_damage & some_other 

myflags.liquid = 1; // change the flags to include the liquid 

if (myflags.takes_damage) // test for one flag 
    apply_damage(); 
if (myflags.liquid && myflags.some_other) // test for multiple flags 
    show_strange_behavior(); 

cette méthode vous permet de définir un certain nombre de drapeaux, mais sans s'y limiter, et vous pouvez étendre votre drapeau fixé à à tout moment sans craindre un débordement. l'inconvénient est que tester un sous-ensemble des drapeaux est plus lourd et nécessite plus de code.

+2

Intéressant. Première fois que je rencontre cette méthode. Je serais intéressé à lire les commentaires de personnes plus informées que moi sur les avantages et les inconvénients par rapport au pouvoir traditionnel de deux enums. Est-ce que cela fonctionne avec tous les compilateurs et toutes les architectures? Quels sont les pièges? – gregschlom

+0

@gregschlom Je voudrais aussi avoir plus de commentaires. il est totalement conforme aux normes, donc il devrait fonctionner avec n'importe quel compilateur. Les seuls pièges que je peux penser sont: 1. Si la structure n'est pas emballée, un ensemble de drapeaux peut prendre une grande quantité de mémoire, dont une grande partie est inutilisée. 2. vous ne pouvez pas spécifier plusieurs valeurs de drapeau à la fois, comme 'myflags = grabbable & liquid' (cependant, cela est possible en utilisant une union contenant la structure et une valeur int, mais cela impose beaucoup plus de contraintes, dépend plus du compilateur et n'est pas le sujet de ce poste) –

+0

Merci! Un autre problème (qui est lié au fait que vous ne pouvez pas tester un sous-ensemble de drapeaux, comme vous l'avez souligné), est que vous ne pouvez pas facilement tester si deux variables d'indicateur sont égales. (ie: vous ne pouvez pas faire: if (myflags == someOtherFlags) {...}). Un peu un bloqueur pour moi, je vais rester avec de vieux enums ... – gregschlom