繁体   English   中英

将枚举与字符串关联的正确方法

[英]A proper way of associating enums with strings

假设我在整个程序中经常使用一些字符串(用于存储状态和类似的东西)。 字符串操作可能很昂贵,所以无论何时解决它们我都想使用枚举。 到目前为止,我已经看到了几个解决方案:

typedef enum {
    STRING_HELLO = 0,
    STRING_WORLD
} string_enum_type;

// Must be in sync with string_enum_type
const char *string_enumerations[] = {
    "Hello",
    "World"
}

我经常遇到的另一个:

typedef enum {
    STRING_HELLO,
    STRING_WORLD
} string_enum_type;

const char *string_enumerations[] = {
    [STRING_HELLO] = "Hello",
    [STRING_WORLD] = "World"
}

这两种方法的缺点是什么? 还有更好的吗?

前者的唯一优势是它与古老的C标准向后兼容。

除此之外,后一种选择是优越的,因为它确保数据完整性,即使枚举被修改或项目改变位置。 但是,它应该通过检查来完成,以确保枚举中的项目数与查找表中的项目数相对应:

typedef enum {
    STRING_HELLO,
    STRING_WORLD,
    STRING_N  // counter
} string_enum_type;

const char *string_enumerations[] = {
    [STRING_HELLO] = "Hello",
    [STRING_WORLD] = "World"
};

_Static_assert(sizeof string_enumerations/sizeof *string_enumerations == STRING_N,
               "string_enum_type does not match string_enumerations");

以上是简单的“枚举 - 查找表”耦合的最佳方法。 另一种选择是使用结构,但这更适合更复杂的数据类型。


最后,更多的是作为旁注,第3版将是使用“X宏”。 除非您对代码重复和维护有特殊要求,否则不建议这样做。 为了完整起见,我会将其包含在此处,但我不建议在一般情况下使用它:

#define STRING_LIST          \
 /* index         str    */  \
  X(STRING_HELLO, "Hello")   \
  X(STRING_WORLD, "World")


typedef enum {
  #define X(index, str) index,
    STRING_LIST
  #undef X
  STRING_N // counter
} string_enum_type;


const char *string_enumerations[] = {
  #define X(index, str) [index] = str,
    STRING_LIST
  #undef X
};

_Static_assert(sizeof string_enumerations/sizeof *string_enumerations == STRING_N,
               "string_enum_type does not match string_enumerations");

另一种可能是使用函数而不是数组:

const char *enumtostring(string_enum_type e) {
    switch(e) {
        case STRING_HELLO: return "hello";
        case STRING_WORLD: return "world";
    }
}

至少gcc会在添加枚举值时发出警告,但忘记添加匹配的switch case。

(我想你也可以尝试inline这种功能。)


附录:我提到的海湾合作委员会警告仅当switch语句没有一个default情况下。 因此,如果你想打印一些以某种方式蠕变的越界值的东西,你可以这样做,而不是default情况,但是有这样的东西:

const char *enumtostring(string_enum_type e) {
    switch(e) {
        case STRING_HELLO: return "hello";
        case STRING_WORLD: return "world";
    }
    return "(unrecognized string_enum_type value)";
}

包含越界值也很好:

    static char tmpbuf[50];
    snprintf(tmpbuf, sizeof(tmpbuf), "(unrecognized string_enum_type value %d)", e);
    return tmpbuf;

(这最后一个片段有一些额外的限制,但是这个附录已经很久了,所以我现在不会对它们提出异议。)

另一种可能性是用户#defines

尽管使用了许多缺点,但主要的好处是#defines不占用空间,除非它们被使用......

#define STRING_HELLO "Hello"
#define STRING_WORLD "World"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM