2015-03-26 3 views
1

Допустим, у меня есть перечисление, которое используется для флагов, т.е.C++ Получить побитовый сдвиг от Enum Value

enum Flags { Flag1 = 1 << 0, Flag2 = 1 << 1, Flag3 = 1 << 2 }; 

Если я даюсь значением Flags, я хотел бы знать, что битое сдвига значения является. По существу, для Flag1 Я хотел бы вернуть 0, для Flag2 Я хотел бы вернуть 1 и т. Д. Может ли кто-нибудь подумать о том, как это сделать, кроме использования функции с оператором switch.

Я хочу этого, потому что у меня есть массив данных, а возвращаемое значение функции является одним из элементов этого массива. Какой элемент для возврата зависит от того, какой флаг передан функции.

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

myobj* objects[3] = { obj1, obj2, obj3 }; 

myobj* GetObj(Flags FlagValue) 
{ 
    switch(FlagValue) 
    { 
     case Flag1: 
      return objects[0]; 
     case Flag2: 
      return objects[1]; 
     case Flag3: 
      return objects[2]; 
    } 
} 

ответ

5

Что вам нужно, это функция, которая учитывает конечные нули в ваших значениях перечисления. Существует множество методов, которые могут это сделать (см. https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear), но большинство современных платформ обычно предоставляют специальную инструкцию CPU, которая делает именно это. Поскольку язык не имеет соответствующего оператора, доступ к этой команде доступен только через нестандартные средства для компилятора, такие как __builtin_ctz в GCC или _BitScanReverse в MSVC.

Однако, если вы только работать с компиляции значения лучше C решение ++ будет что-то вдоль линий

template <unsigned N> struct ctz { 
    static const unsigned value = ctz<N - 1>::value + 1; 
}; 

template <> struct ctz<1> { 
    static const unsigned value = 0; 
}; 

template <> struct ctz<0>; // Left undefined 

... 
cout << ctz<Flag2>::value << endl; 
+0

Я поселился на '_BitScanReverse'. Кажется, это самая краткая и лучшая производительность. Поскольку это то, что потенциально называют часто, мне нравится метод без циклов. Даже если внутри '_BitScanReverse' есть цикл, он, вероятно, оптимизирован лучше, чем я пишу цикл while с проверкой ошибок. –

1

Что-то, как это должно работать:

int iFlag = (int)FlagValue; 
int ix = 0; 
while (iFlag > 1) { 
    ix++; 
    iFlag = iFlag >> 1; 
} 
return objects[ix]; 

Чтобы обезопасить себя, добавить чеки за незаконный вход как FlagValue <= 0 или FlagValue >= (sizeof(objects)/sizeof(objects[0])).

2

Наиболее кратким (и, вероятно, правильный) путь, чтобы реорганизовать дизайн , «Флаг» на самом деле не флаг - это индекс, поэтому код с индексом.

enum Index { I1, I2, I3, INDEX_COUNT }; 

myobj* objects[INDEX_COUNT] = { obj1, obj2, obj3 }; 

myobj* GetObj(Index i) 
{ 
    assert(i < INDEX_COUNT); 
    return objects[i]; 
} 
Смежные вопросы