10

如果我有多个enum,例如:

 enum Greetings{ hello, bye, how };

 enum Testing { one, two, three };

我怎样才能强制使用正确的enum?例如,为了更好的调试和可读性,我不希望有人hello在他们应该使用的时候使用。one

4

6 回答 6

15

在 C 中,您可以使用样板代码来伪造它。

typedef enum { HELLO_E, GOODBYE_E } greetings_t;
struct greetings { greetings_t greetings; };
#define HELLO ((struct greetings){HELLO_E})
#define GOODBYE ((struct greetings){GOODBYE_E})

typedef enum { ONE_E, TWO_E } number_t;
struct number { number_t number; };
#define ONE ((struct number){ONE_E})
#define TWO ((struct number){TWO_E})

void takes_greeting(struct greetings g);
void takes_number(struct number n);

void test()
{
    takes_greeting(HELLO);
    takes_number(ONE);

    takes_greeting(TWO);
    takes_number(GOODBYE);
}

这不应该产生任何开销,并且会产生错误而不是警告:

$ gcc -c -std=c99 -Wall -Wextra test2.c
test2.c:在函数“测试”中:
test2.c:19:错误:“takes_greeting”的参数 1 的类型不兼容
test2.c:20:错误:“takes_number”的参数 1 的类型不兼容

请注意,我没有使用 GNU 扩展,并且不会生成虚假警告。只有错误。另请注意,我使用的 GCC 版本与泥土一样古老,

$ gcc --版本
powerpc-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5493)
版权所有 (C) 2005 Free Software Foundation, Inc.
这是免费软件;查看复制条件的来源。没有
保修单; 甚至不考虑适销性或特定用途的适用性。

这应该适用于任何支持 C99 复合文字的编译器。

于 2013-02-11T23:09:23.630 回答
11

Clang produces the following warning, which is the best you can do (Although the user could upgrade the warning to an error).

enum Greetings { hello, bye, how };
enum Count { one, two, three };

void takes_greeting(enum Greetings x) {}
void takes_count(enum Count x) {}

int main() {
    takes_greeting(one);
    takes_count(hello);
}

Compiler output:

cc     foo.c   -o foo
foo.c:8:17: warning: implicit conversion from enumeration type 'enum Count' to different enumeration type 'enum Greetings' [-Wenum-conversion]
        takes_greeting(one);
        ~~~~~~~~~~~~~~ ^~~
foo.c:9:14: warning: implicit conversion from enumeration type 'enum Greetings' to different enumeration type 'enum Count' [-Wenum-conversion]
        takes_count(hello);
        ~~~~~~~~~~~ ^~~~~

If users are going to ignore errors and warnings from the compiler, then there's not much you can do to help them.

于 2013-02-11T22:48:54.863 回答
4

不幸enum的是,这是 C 类型系统中的一个弱点。类型的变量属于enumenum类型,但您使用 声明的常量enum属于 type int

所以在你的例子中

enum Greetings{ hello, bye, how };
enum Testing { one, two, three };

enum Greetings const holla = hello;
enum Testing const eins = one;

helloone是相同值的两个名称,即0和 具有相同的类型int

hollaeins再次具有 value 0,但具有各自的类型。

如果您想为“真实”常量强制一些“官方”类型安全,即具有您想要的类型和值的实体,您必须使用一些更复杂的构造:

#define GREETING(VAL) ((enum Greetings){ 0 } = (VAL))
#define HELLO GREETING(hello)

宏中的赋值GREETING确保结果是一个“右值”,因此它不能被修改,编译器只会根据它的类型和值来获取它。

于 2013-02-11T23:09:20.267 回答
3

这是你不想听到的答案。在 C 中,你真的不能。现在,如果您的 C 代码位于 C++ 的“清洁 C”子集中,您可以使用 C++ 编译器进行编译,以获取使用错误枚举/整数值等的所有错误。

于 2013-02-11T22:59:48.757 回答
2

如果您还想确保有效范围,那么有一种技术会带来用于获取整数值的指针取消引用的少量开销——以及大量的样板输入。它可能仍然有用,因为它利用您编写范围检查代码,否则它是必要的。

问候.h:

#ifndef GREETINGS_H
#define GREETINGS_H

struct greetings;
typedef struct greetings Greetings;

extern const Greetings * const Greetings_hello;
extern const Greetings * const Greetings_bye;
extern const Greetings * const Greetings_how;

const char *Greetings_str(const Greetings *g);
int Greetings_int(const Greetings *g);

#endif

问候.c:

#include "greetings.h"

struct greetings {
    const int val;
};

static const Greetings hello = { 0 };
static const Greetings bye = { 1 };
static const Greetings how = { 2 };

const Greetings * const Greetings_hello = &hello;
const Greetings * const Greetings_bye = &bye;
const Greetings * const Greetings_how = &how;

static const char * const Greetings_names[] = {
    "hello",
    "bye",
    "how"
};

const char *
Greetings_str(const Greetings *g)
{
    return Greetings_names[g->val];
}

int
Greetings_int(const Greetings *g)
{
    return g->val;
}

示例 main.c:

#include <stdio.h>
#include "greetings.h"

void
printTest(const Greetings *greeting)
{
    if (greeting == Greetings_how) return;
    puts(Greetings_str(greeting));
}

int
main()
{
    const Greetings *g = Greetings_hello;
    printTest(g);
}

是的,要输入很多,但是您可以获得完整的类型和范围安全性。没有其他编译单元可以实例化 a struct greetings,所以你是完全安全的。


编辑 2015-07-04:为了防止 NULL,有两种可能性。要么使用 NULL 作为默认值(#define Greetings_hello 0而不是现在使用的指针)。这非常方便,但会降低默认枚举值的类型安全性,NULL 可用于任何枚举。或者声明它无效,然后在访问器方法中检查它,返回错误,或者使用 GCC 之类的东西__attribute__((nonnull()))在编译时捕获它,例如在 greetings.h 中:

const char *Greetings_str(const Greetings *g)
        __attribute__((nonnull(1)));
于 2015-07-01T15:20:39.357 回答
-3

You can typedef your enums and then declare variables and function arguments of those types.

于 2013-02-11T22:48:32.803 回答