控制枚举值的可见性

时间:2011-04-01 16:41:08

标签: c++ arrays enums visibility

考虑一个C ++类,它导出一个枚举,在该枚举上维护一个内部数组,并希望导出一个接受枚举值的命令。

class foo {
public:
  enum color {
    red,
    yellow,
    green,
    NUM_COLORS
  };
private:
  something somebody[NUM_COLORS];
public:
  void command(color c);
};

是否有一种干净的方法只能导出实际颜色,而不是NUM_COLORS?当编译器的类型系统真的应该能够为我做这件事时,我不想在每次调用时检查边缘情况。

明显的黑客是:

class foo {
public:
  enum color {
    red,
    yellow,
    green
  };
private:
  /* something like */ const unsigned NUM_COLORS = green+1;
  unsigned LEDs_in_stock[NUM_COLORS];
public:
  void command(color c);
};

这当然是一个滴答作响的定时炸弹,等待一些可怜的过度工作的维护程序员添加蓝色LED的配置,并忘记更新NUM_COLORS行。

让我澄清一下。在这种特殊情况下,我想要的是能够说:

class foo {
public:
  enum color {
    red,
    yellow,
    green
  };
  void command(color c);
private:
  something somebody[color];
};

据我所知,C ++不允许这样做。

4 个答案:

答案 0 :(得分:3)

将枚举放入基类中是一个选项吗?

class foo_enums {
public:
  enum color {
    red,
    yellow,
    green,
    NUM_COLORS
  };

protected:
  foo_enums() { }
  ~foo_enums() { }
};

class foo : public foo_enums {
private:
  unsigned LEDs_in_stock[NUM_COLORS];

  /* make NUM_* values inaccessible */
  using foo_enums::NUM_COLORS;

public:
  void command(color c);
};

我个人不会这样做,因为它看起来像是一个过于复杂的工作。我只是禁止来电者通过NUM_COLORS。没错,类型系统不会检查。但对于检查人类程序员来说,这肯定是一件容易的事情。他们为什么会通过NUM_COLORS

答案 1 :(得分:2)

我的第一个想法是尝试解决问题,但在经过一番反思后,我会将负担转移到command

void command(color c) {
  assert(0 <= c && c < NUM_COLORS && "Invalid argument");
}

由于枚举是如此弱的类型,你需要检查输入,因为任何人都可以轻松提供蹩脚的参数:

Foo foo;
foo.command(static_cast<Foo::color>(3)); // 3 is green, right ?

原始解决方案:

class Foo {
  struct impl { enum { red, yellow, green, NUM_COLORS }; };
public:
  enum color { red = impl::red, yellow = impl::yellow, green = impl::green };

  void command(color c);
};

不幸的是,有很多重复(我实际上原来键入green = impl::yellow;但是如果你从不直接引用impl的值那么无关紧要。)

否则,总有一个宏技巧:

#define MY_DEFINE_ENUM(Type, Elements)       \
  enum Type { BOOST_PP_SEQ_ENUM(Elements) }; \
  inline size_t size(Type) { return BOOST_PP_SEQ_SIZE(Elements); }

使用邪恶的宏和模糊的预处理器机制来避免代码重复。它显然只适用于连续的枚举元素(它返回元素的数量,而不是最大数量)。

答案 2 :(得分:1)

在保护未来的维护者之间做出简单/容易的错误,并尝试阻止明显错误的事情,例如使用NUM_COLORS值,这是一条很好的界限。

在你的情况下,我建议在关键函数处断言输入并将其保留在那里。

我相信您可以使用专门的模板代理类和NUM_COLORS上的static_assert来阻止用户将其传递到您的函数中。

我打出了似乎有用的东西。

class foo {
public:
  enum color {
    red,
    yellow,
    green,
    NUM_COLORS
  };

  class Color_Rep
  {
    color c;
  protected:
    Color_Rep(color which_color) : c(which_color) { }
  };

  template <color C>
  struct Color : public Color_Rep
  {
    Color() : Color_Rep(C) { }
    enum { value = C };
  };

private:
  int bar[NUM_COLORS];

public:
  void command(Color_Rep c);
};

// Deny access to command(NUM_COLORS).
template <>
struct foo::Color<foo::NUM_COLORS>
{
};

int main()
{
    foo().command(foo::Color<foo::red>());
    foo().command(foo::Color<foo::green>());
    foo().command(foo::Color<foo::NUM_COLORS>());  // Won't compile.
}

答案 3 :(得分:0)

解决方案是使用地图:

std::map<color, unsigned> LEDs_in_stock;

LEDs_in_stock[red] += 2;

LEDs_in_stock[red]; // = 2
LEDs_in_stock[green]; // = 0

通过这种方式,您可以保持简洁,不需要硬编码任何大小。