инвертирующее значение enum

У меня перечисление объявлено так:

enum class ShootingDirection
{
Down,
Up,
Right,
Left
};

Я также объявил член класса этого типа, который должен быть инвертирован в некотором методе, не имеет значения, почему и где. Под инвертированным я подразумеваю «Вниз» -> «Вверх» (также наоборот) и «Вправо» -> «Влево» (опять же, наоборот).
Вместо переключателя или группы if … else if я использовал карту, объявленную так:

std::map<ShootingDirection, ShootingDirection> _invertedDirectionsMap;

Он заполнен такими данными:

_invertedDirectionsMap[ShootingDirection::Down] = ShootingDirection::Up;
_invertedDirectionsMap[ShootingDirection::Up] = ShootingDirection::Down;
_invertedDirectionsMap[ShootingDirection::Right] = ShootingDirection::Left;
_invertedDirectionsMap[ShootingDirection::Left] = ShootingDirection::Right;

Инверсия переменной, тип которой ShootingDirection, тогда довольно проста (при условии, что она инициализирована):

_direction = _invertedDirectionsMap[_direction];

Я думаю, что это глупое использование карты и ненужные накладные расходы. Есть ли умнее?
Кстати, этот вопрос относится к Code Review или здесь? Я не очень знаком с критериями.

1

Решение

Я обычно выкладываю перечисления направлений по кругу (в основном совпадая с тем, как будет измеряться угол от оси x. Итак:

enum ShootingDirection { Right, Down, Left, Up };

соответствие 0 градусов, 90 градусов, 180 градусов, 270 градусов. Это (для меня) делает это интуитивно понятным макетом.

Тогда обратное направление просто: (dir + 2) % 4, Или более полно:

int InvertDirection(int dir)
{
return (dir + 2) % 4;
}

Мне это нравится, потому что я нахожу его довольно интуитивным в использовании. Поворот по часовой стрелке (dir + 1) % 4, против часовой стрелки (dir + 3) % 4,

Он также может быть легко расширен до нескольких направлений. Я использую его для шестиугольных направлений: теперь перевернутое направление (dir + 3) % 6,

4

Другие решения

Да: использовать switch утверждение вместо. Это будет значительно эффективнее. Если этого недостаточно, подумайте об этом:

enum class ShootingDirection
{
Down = 1,
Up = -1,
Right = 2,
Left = -2
};

Затем вы можете инвертировать путем арифметического отрицания (и, конечно, приведение к / из int).

2

Кто-то уже начал подразумевать это решение, но это довольно быстро и позволяет избежать оператора switch, который, как вы сказали, вам не нужен:

enum ShootingDirection {
Down = -2,
Up = 2,
Right = 1,
Left = -1
};

inline ShootingDirection invert(ShootingDirection d) {
return static_cast<ShootingDirection>(-static_cast<int>(d));
}

В идеале, это должно компилировать только одну инструкцию по сборке.

2

enum class Direction {
UP = 1,
DOWN = 2,
LEFT = 3,
RIGHT = 4
};

enum class InvertedDirection {
UP = 2,
DOWN = 1,
LEFT = 4,
RIGHT = 3
};

InvertedDirection getInvertedDirection(Direction dir) {
return static_cast<InvertedDirection>(dir);
}

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

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

1

Есть способ сделать это без ветвления.

http://coliru.stacked-crooked.com/a/4b0a1e3c58b74004

Хитрость заключается в следующем:

/* The direction type is 8 bits, only the first 2 are used */
/* The first 2 bits have the following meanings */
typedef unsigned char direction;
const direction down  = '\x00'; /* 00000000 */
const direction up    = '\x03'; /* 00000011 */
const direction left  = '\x01'; /* 00000001 */
const direction right = '\x02'; /* 00000010 */
/* All other values are interpreted by first zeroing out the last 6 bits */

/* Use to zero out all bits but the last two, also for XORing */
static const direction oneone = '\x03';

inline direction xorflip(const direction d)
{
/*
* XOR with 11
* 00 ^ 11 = 11, down  to up
* 01 ^ 11 = 10, left  to right
* 10 ^ 11 = 01, right to left
* 11 ^ 11 = 00, up    to down
*/
return d ^ oneone;
}
1

Вы спрашивали:

Есть ли умнее?

Вот еще один подход к изменению направления:

#include <iostream>

enum class ShootingDirection : unsigned char
{
Up    = 0x00,
Down  = 0xFF,
Left  = 0x01,
Right = 0xFE
};

ShootingDirection reverseDirection(ShootingDirection dir)
{
return ShootingDirection((unsigned char)dir ^ 0xFF);
}

int main()
{
ShootingDirection up = ShootingDirection::Up;
ShootingDirection down = ShootingDirection::Down;
ShootingDirection left = ShootingDirection::Left;
ShootingDirection right = ShootingDirection::Right;

std::cout << "Up: " << (int)up << ", Reverse: " << (int)reverseDirection(up) << std::endl;
std::cout << "Down: " << (int)down << ", Reverse: " << (int)reverseDirection(down) << std::endl;
std::cout << "Left: " << (int)left << ", Reverse: " << (int)reverseDirection(left) << std::endl;
std::cout << "Right: " << (int)right << ", Reverse: " << (int)reverseDirection(right) << std::endl;

return 0;
}

Выход:

Up: 0, Reverse: 255
Down: 255, Reverse: 0
Left: 1, Reverse: 254
Right: 254, Reverse: 1
1
По вопросам рекламы [email protected]