枚举映射强大到重构

时间:2015-02-19 08:58:19

标签: c++ enums c++14

我想将(作用域)枚举的值映射到其他一些值。例如,我在这里将Color映射到其他枚举Group

enum class Color {
  Red, Green, Blue, Cyan, Magenta, Yellow, White, Black, 
  COUNT  // Needed to know the number of items
};
enum class Group {
  Primary, Secondary, Neutral
};

Group GetGroupOfColor(Color color);  // Mapping function I'm going to implement

我想确保如果有人更改Color枚举中的元素数量,则此函数将无法编译。

我想出了解决这个问题的唯一方法:

Group GetGroupOfColor(Color color)
{
  static const Group color2group[] = {
    Group::Primary,    // Red
    Group::Primary,    // Green
    Group::Primary,    // Blue
    Group::Secondary,  // Cyan
    Group::Secondary,  // Magenta
    Group::Secondary,  // Yellow
    Group::Neutral,    // White
    Group::Neutral     // Black
  };
  static_assert(ARRAY_SIZE(color2group) == size_t(Color::COUNT), "DEADBEEF!");

  auto index = size_t(color);
  assert(index < size_t(Color::COUNT));
  return color2group[index];
}

其中ARRAY_SIZE可以像下面这样实现:

template <typename T, size_t N>
constexpr size_t ARRAY_SIZE(T(&)[N])
{
  return N;
}

这个实现做了我想要的,但它有一些缺点:

  • COUNT枚举中添加这个丑陋的Color项目(最让我烦恼)
  • 如果有人重新排序Color
  • 的内容,则会无声地失败
  • 不适用于不连续的枚举,即具有明确指定的值(这一点并不重要)

我的问题是,是否有办法改进此实施?也许我甚至没有考虑过一些不同的方法。也许有其自身的缺点,我会发现不那么讨厌。

另见:

3 个答案:

答案 0 :(得分:3)

我会使用switch语句。

switch (colour) {
    case Colour::Red:   return Group::Primary;
    //...
    case Colour::Black: return Group::Neutral;
}
return Group::Invalid;  // or throw, assert, or whatever.

这应该涵盖您的所有需求:

  

COUNT中添加这个丑陋的Color enum项目(最让我烦恼)

不需要,每个枚举器只需case

  

如果某人重新排序Color

的内容,则会无声地失败

每个case都是明确命名的,因此每个枚举器的值无关紧要(只要它们是唯一的;但如果不是这样,你就会收到错误。)

  

不适用于不连续的枚举

同样,名为case的语句并不关心实际值。

  

如果有人更改Color enum中的元素数量,则此函数将无法编译

虽然不能保证,但大多数编译器应该能够在交换机中有未处理的枚举器时发出警告(只要没有default分支)。对于GCC,如果您希望它导致失败,则选项为-Wswitch(包含在-Wall中)和-Werror

答案 1 :(得分:1)

稍微不同的方法 - 创建一种丰富的枚举:

class Color {
  public :
    enum class Name { RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW, WHITE, BLACK };
    enum class Group { PRIMARY, SECONDARY, NEUTRAL };

    uint32_t rgb;
    Name name;
    Group group;

    static const Color Red;
    static const Color Green;
    static const Color Blue;
    static const Color Cyan;
    static const Color Magenta;
    static const Color Yellow;
    static const Color White;
    static const Color Black;

  private :
    Color(uint32_t rgb, Name name, Group group) : rgb(rgb), name(name), group(group) { }

  public :
    inline operator const Name() const { return name; }
};

const Color Color::Red     = Color(0xFF0000, Color::Name::RED,     Color::Group::PRIMARY);
const Color Color::Green   = Color(0x00FF00, Color::Name::GREEN,   Color::Group::PRIMARY);
const Color Color::Blue    = Color(0x0000FF, Color::Name::BLUE,    Color::Group::PRIMARY);
const Color Color::Cyan    = Color(0x00FFFF, Color::Name::CYAN,    Color::Group::SECONDARY);
const Color Color::Magenta = Color(0xFF00FF, Color::Name::MAGENTA, Color::Group::SECONDARY);
const Color Color::Yellow  = Color(0xFFFF00, Color::Name::YELLOW,  Color::Group::SECONDARY);
const Color Color::White   = Color(0xFFFFFF, Color::Name::WHITE,   Color::Group::NEUTRAL);
const Color Color::Black   = Color(0x000000, Color::Name::BLACK,   Color::Group::NEUTRAL);

然后可以像这样使用:

void fun(const Color& color) {
    switch (color) {
        case Color::Name::RED   : std::cout << "red"; break;
        case Color::Name::GREEN : std::cout << "green"; break;
        case Color::Name::BLUE  : std::cout << "blue"; break;
        // etc.
    }
    std::cout << " ";
    switch (color.group) {
        case Color::Group::PRIMARY   : std::cout << "primary"; break;
        case Color::Group::SECONDARY : std::cout << "secondary"; break;
        case Color::Group::NEUTRAL   : std::cout << "neutral"; break;
    }
    std::cout << " : " << std::hex << color.rgb << std::endl;
}

答案 2 :(得分:1)

我怀疑您是否想要检测Color枚举的更改,因为您想确保每种颜色都可以始终映射到某个组?如果是这样,您可以使用某些宏建立映射,这使得无法在不定义映射到的内容的情况下添加新的Color值。类似的东西:

#define COLORMAP \
  MAP_COLOR(Red, Primary) \
  MAP_COLOR(Green, Primary) \
  MAP_COLOR(Blue, Primary) \
  MAP_COLOR(Cyan, Secondary) \
  MAP_COLOR(Magenta, Secondary) \
  MAP_COLOR(Yellow, Secondary) \
  MAP_COLOR(White, Neutral) \
  MAP_COLOR(Black, Neutral)

然后适当地定义MAP_COLOR将允许您从单个来源定义Color枚举以及映射函数:

#define MAP_COLOR(a, b) a,
enum class Color {
  COLORMAP
  Invalid
};
#undef MAP_COLOR

enum class Group {
  Primary, Secondary, Neutral, Invalid
};

#define MAP_COLOR(a, b) case Color::a: return Group::b;
Group GetGroupOfColor(Color color)
{
  switch (color) {
    COLORMAP
    case Color::Invalid: return Group::Invalid;
  }
  return Group::Invalid;
}