Флаги, enum (C) - PullRequest
       35

Флаги, enum (C)

13 голосов
/ 27 октября 2009

Я не очень привык программировать с флагами, но, думаю, я просто нашел ситуацию, в которой они были бы полезны:

У меня есть пара объектов, которые регистрируются как слушатели определенных событий. Какие события они регистрируют, зависит от переменной, которая отправляется им при их создании. Я думаю, что хороший способ сделать это - послать побитовые ИЛИ связанные переменные, например: TAKES_DAMAGE | Хватает | LIQUID и т. Д. Затем в конструкторе объект может проверить, какие флаги установлены, и самостоятельно зарегистрировать их в качестве слушателей.

Но это то, где я запутался. Предпочтительно, чтобы флаги были в перечислении. Но это тоже проблема. Если у нас есть эти флаги:

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

Тогда отправка флага SOME_OTHER (3) будет аналогична отправке GRABBABLE | ЖИДКОСТЬ, не так ли?

Как именно вы справляетесь с этим?

Ответы [ 6 ]

51 голосов
/ 27 октября 2009

Ваше перечисление должно быть степенью двойки:

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

Или более читабельным образом:

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

Почему? Потому что вы хотите иметь возможность комбинировать флаги без перекрытия, а также иметь возможность сделать это:

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

... Что работает, если значения перечисления выглядят так:

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

Итак, допустим, вы установили myVar на GRABBABLE | TAKES_DAMAGE, вот как это работает, когда вам нужно проверить флаг GRABBABLE:

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

Если вы установите myVar в LIQUID | SOME_OTHER, операция приведет к:

 myVar:     00001100
 GRABBABLE: 00000010 [AND]
 -------------------
            00000000 // zero => converts to false
28 голосов
/ 27 октября 2009

другой способ хранения флагов - вообще не беспокоиться о базовом типе. при использовании перечисления значения перечисления по умолчанию сохраняются в беззнаковом int, который составляет 32 бита на обычном компьютере. это дает вам только 32 возможных флага: хотя, конечно, много, в некоторых случаях этого недостаточно.

Теперь вы можете определить свой флаг, установленный следующим образом:

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

если вы никогда не сталкивались с этим, часть ': 1' говорит компилятору использовать только 1 бит для хранения этого члена структуры.

теперь вы можете определить переменную для хранения флагов и работать с этими флагами:

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

этот метод позволяет вам определять любое количество флагов без ограничений, и вы можете расширить свой набор флагов в любое время, не опасаясь переполнения. недостатком является то, что тестирование подмножества флагов является более громоздким и требует больше кода.

5 голосов
/ 27 октября 2009

Да. Вместо этого сделайте ваши члены перечисления полномочиями 2:

enum
{
    TAKES_DAMAGE = (1 << 0),
    GRABBABLE = (1 << 1),
    LIQUID = (1 << 2),
    SOME_OTHER = (1 << 3)
};
4 голосов
/ 27 октября 2009

Разве вы не можете просто установить значения в перечислении?

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

После этого просто поразмышляйте над ними ИЛИ.

4 голосов
/ 27 октября 2009

Вы должны делать флаги только степенью двойки, т. Е. Каждый имеет бит в любом типе данных, в котором вы храните это, и ничто не перекрывается, когда вы побитовое ИЛИ.

3 голосов
/ 27 октября 2009

вам нужно

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