Lukas Salich
Lukas Salich

Reputation: 1000

Neat way how to cyclically iterate 4 enum class values in both directions in C++?

I have:

enum class orientation {
  North,
  East,
  South,
  West
};

I want to rotate its instance left (North => West) and right (West => North).
But I don't want to convert them to numbers, because it harms readability and intention and also jumping from the last number to first and back is strange.

I came up with lots of solutions, but all are kind of lame :(

Upvotes: 7

Views: 591

Answers (3)

cigien
cigien

Reputation: 60238

Here's a solution that doesn't do any casting, and is quite easy to read:

constexpr auto rotate(orientation o, int n) -> orientation
{
  n = (n + 4) % 4; // for negative rotations  
  if (!n) 
    return o;
  switch (o)
  {
    case orientation::North : return rotate(orientation::East, n - 1);
    case orientation::East  : return rotate(orientation::South, n - 1);
    case orientation::South : return rotate(orientation::West, n - 1);
    case orientation::West  : return rotate(orientation::North, n - 1);
  }
}

The major advantage to this solution is that it's robust to someone coming along and changing the order of the members in the enum class.

Upvotes: 3

Marshall Clow
Marshall Clow

Reputation: 16680

Make a class called Orientation (say). Give it a member variable of the enumeration (orientation). Define a getter/setter, and increment and decrement operators (or rotate_left and rotate_right if you like those names better). Make this all implementation details inside the class.

You can use the logic that Barry suggested; but bury it in the class where no one has to deal with it.

Upvotes: 1

Barry
Barry

Reputation: 303367

Since they're in order:

constexpr auto rotate(orientation o, int n) -> orientation {
    // convert to int
    int dir = (int)o;
    // rotate as an int
    dir = (dir + n) % 4;
    // account for negatives
    if (dir < 0) {
        dir += 4;
    }
    // and then convert back
    return orientation{dir};
}

Which you can check:

static_assert(rotate(orientation::North, 1) == orientation::East);
static_assert(rotate(orientation::North, -1) == orientation::West);

I picked the integer to mean "number of 90 degree turns right" but you can adjust as suitable for your actual problem. Or add helper functions like:

constexpr auto rotate_left(orientation o) -> orientation {
    return rotate(o, -1);
}

constexpr auto rotate_right(orientation o) -> orientation {
    return rotate(o, 1);
}

Upvotes: 9

Related Questions