Оператор переключения битовой маски - PullRequest
6 голосов
/ 07 июля 2011

У меня есть этот код в разделе моего проекта:

enum myEnum
{
    invalid = -1,
    val1 = 1,
    val2 = 2,
    val3 = 4
};

int bitmask = val1 | val3;

if(bitmask & val1)
    ...
if(bitmask & val2)
    ...
if(bitmask & val3)
    ...

Это нормально, и работает отлично, но мне всегда было интересно, можно ли это сделать с помощью переключателя. Я думал что-то вроде этого:

int checkMask(int& mask)
{
    for(int i = 0; mask; mask &= ~(1 << i++))
    {
        if(mask & (1 << i))
        {
            int ret = mask & (1 << i);
            mask &= ~ret;
            return ret;
        }
    }

    return invalid;
}

#define START_BITMASK_SWITCH(x) int xcopy = x; while(xcopy) { switch(checkMask(xcopy))
#define END_BITMASK_SWITCH };

int bitmask = val1 | val3;

START_BITMASK_SWITCH(bitmask)
{
    case val1:
        ...
        break;
    case val2:
        ...
        break;
    case val3:
        ...
        break;
}
END_BITMASK_SWITCH

так что мои вопросы:

я только что решил свою проблему? Я полагаю, у меня есть, но это чистое решение? Есть ли более простой способ сделать это? это плохая идея смешивать #defines и функции?

Ответы [ 6 ]

12 голосов
/ 07 июля 2011

Нет, это не чистое решение, и для вашего контекста вы можете избежать смешивания #define и функций. Вы можете попробовать следующее решение, если вы хотите switch():

int bitmask = val1 | val3;
int mask = 1;
while(bitmask)
{
  switch(bitmask & mask)
  {
  case val1: ... break;
  case val2: ... break;
  case val4: ... break;
  case val8: ... break;
  }
  bitmask &= ~mask; 
  mask <<= 1;
}
6 голосов
/ 07 июля 2011

Нет, это (очевидно) не чистое решение.Ваш исходный код был простым, не зацикливался и не включал «секретные» макросы специального случая, которые добавляют в язык странные конструкции.

Под «странной конструкцией» я подразумевал START_BITMASK_SWITCH() / END_BITMASK_SWITCH макросы, которые:

  • Добавить цикл без использования каких-либо стандартных ключевых слов, чтобы даже намекнуть, что цикл происходит
  • Clobber имен в текущей области more илименее тихо
  • Включите ложную точку с запятой

Ваше решение не принесет никакой пользы, все, что он делает, это добавляет раздувание и накладные расходы (как с точки зрения размера кода, так и сложности, ипроизводительность во время выполнения), просто чтобы по какой-то причине захотеть использовать switch для выполнения чего-то, что не очень подходит для этого.

Очевидно, это очень субъективно, но вы сделалиспрашивайте.

4 голосов
/ 07 июля 2011

Я вижу несколько проблем:

  • добавляет препроцессор без особой выгоды
  • добавляет много медленного кода (сдвиги, циклы, тесты)
  • он не позволяет добавлять специальные случаи, такие как «если бит 2 включен и бит 3 выключен» (if ((bitmask & (val2 | val3)) == val2))
  • компилятор упустит практически все возможности для оптимизации сгенерированного кода

Это также можно сделать намного проще:

#define START_BITMASK_SWITCH(x) \
    for (uint64_t bit = 1; x >= bit; bit *= 2) if (x & bit) switch (bit)

int bitmask = val1 | val3;

START_BITMASK_SWITCH(bitmask)
{
    case val1:
        ...
        break;
    case val2:
        ...
        break;
    case val3:
        ...
        break;
}
3 голосов
/ 07 июля 2011

Битовая маска - это просто массив bools, если хотите, и ваши перечисления являются индексами. Можете ли вы переключить массив bool? Нет, вы не можете, потому что он может представлять несколько состояний одновременно. Вы можете переключать только общую битовую маску, как с любым целым числом.

0 голосов
/ 07 июля 2011

Вы можете создать конструкцию foreach, в которой вы будете перебирать биты вашей битовой маски и предоставлять функцию с оператором switch.

0 голосов
/ 07 июля 2011
enum Positions {
    ALPHA,
    BETA,
    GAMMA
};

enum Flag {
    ALPHA_FLAG == 1 << ALPHA,
    BETA_FLAG  == 1 << BETA,
    GAMMA_FLAG == 1 << GAMMA
};

Position position_of (Flag f) {
    unsigned n = f;
    unsigned i = 0;
    while ((n & 1) == 0) {
         ++i;
         n >>= 1;
    }
    return Position (i);
}

switch (position_of (flag)) {
    case ALPHA:
    case BETA:
    // ...
};

Это лучше с C ++ 0x сильными перечислениями , тогда вы можете иметь Position::ALPHA и Flag::ALPHA для более четкого именования. Вы также можете использовать constexpr для надежной маскировки значений вашего флага.

...