2011-04-01 62 views
5

考虑一个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++不允许这样做。

+1

据我所知,你试图做的事情是不可能的。想到的下一个最好的事情是非常努力地按住shift键,并在枚举之下写一个非常重要的注释。 – 2011-04-01 16:46:33

+0

编译器不会帮你从愚蠢的。无论如何,有人可以很容易地写foo :: color(7)。 – 2011-04-01 17:08:54

回答

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); } 

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

+0

这看起来就像我要在C++中想要的一样。它还向我展示了如何解决想要导出一些但不是全部的泛化(我期望会很痛苦),而不一定要使用连续的枚举文字。似乎有一些C++设计委员会并没有理解。 – 2011-04-02 03:39:59

+0

@John R. Strohm:我同意,C++中的'enum'有些失效。嵌入了两个概念:只指定枚举的能力和将值映射到名称的能力。它们在语义上是不同的,把它们放在一个单一的概念中会使它变得尴尬(即没有内省,我不明白为什么它们不提供编译时反省,因为它是免费的)。 – 2011-04-02 09:51:51

+0

我对内省,编译时或其他方面的知识不够直接发表评论。我注意到,自从1983年正式发布语言的第一个版本以来,我想要的功能已经在Ada中。 – 2011-04-02 12:34:40

0

一个解决办法是使用地图:

std::map<color, unsigned> LEDs_in_stock; 

LEDs_in_stock[red] += 2; 

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

这样,你让你枚举干净,不需要硬编码任何尺寸。

+0

我看了一下地图头文件,乍看起来好像带了很多行李。我需要把测试程序放在一起,看看实际产生了什么样的代码。我不能忍受一个为简单数组引用强加数百或数千指令开销的解决方案,我不应该这么做。 – 2011-04-01 17:23:08

+0

鉴于地图节点在其有效负载方面的开销,它看起来似乎矫枉过正。 – 2011-04-01 17:25:25

+0

当然它更重,但我认为C++代码比二进制代码更重要。最好有一个非常简单,安全和有效的C++代码来生成一个更大的二进制文件,因为处理器执行它并不是一件痛苦的事情。对于程序员来说,维护一个hacky代码是一件痛苦的事情。这就是我选择personnaly的解决方案,但这取决于你当然^^ – 2011-04-01 17:33:05

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

+0

下一个问题:你如何隐藏'foo_enums'?任何人都可以使用'foo_enums :: NUM_COLORS' ... – 2011-04-01 16:57:45

+0

@André:将它放入一个'detail'命名空间,每个理智的程序员都知道他们甚至不应该想到触及它。 – Xeo 2011-04-01 17:01:42

+1

@Andre任何人都可以解引用空指针。我们如何阻止他们这样做?如果他们无法阅读并按照评论说:“这个枚举常数只能用于class”foo“,那么这太遗憾了。引用Herb Sutter:*记得区分”防止墨菲与保护马基雅维利“。 * – 2011-04-01 17:03:46

1

在保护您的未来维护者免于犯简单/容易错误,并试图阻止某些应该是明显是错误(如使用NUM_COLORS值)之间存在一条细线。

在你的情况下,我会建议在关键函数声明输入并将其留在那里。

我相信你可以使用专门的模板代理类和static_assert S于NUM_COLORS,以防止用户将其传递到你的函数。

我输入了一些似乎可行的东西。

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. 
} 
+0

如果我已经学到了一件事情,那就是期望人们不要做某件事*明显*错误是一种愚蠢的行为,他们会这样做,保证,除非你阻止他们。例如:北电网络软件集团有一个书面的硬性和快速的无例外政策“你应该在提领指针“。猜猜一些无名编码器做了什么?(猜猜谁是追踪由此造成的间歇性硬碰撞?) – 2011-04-01 17:10:10

+0

@John R.斯特罗姆我个人认为北电的例子与这种情况完全不同。很容易忘记检查一个空指针(我不会认为它是个好主意),但是在我的脑海里,使用枚举值'NUM_COLORS'完全不同:编码器明确决定使用它。我们是否经历了一些挫折,试图阻止人们从“#定义私人公共”? – 2011-04-01 17:16:28