简体   繁体   English

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

[英]A proper way of associating enums with strings

Let's say I have a number of strings I use often throughout my program (to store state and things like that). 假设我在整个程序中经常使用一些字符串(用于存储状态和类似的东西)。 String operations can be expensive, so whenever addressing them I'd like to use an enumeration. 字符串操作可能很昂贵,所以无论何时解决它们我都想使用枚举。 I've seen a couple solutions so far: 到目前为止,我已经看到了几个解决方案:

typedef enum {
    STRING_HELLO = 0,
    STRING_WORLD
} string_enum_type;

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

The other one I encounter quite often: 我经常遇到的另一个:

typedef enum {
    STRING_HELLO,
    STRING_WORLD
} string_enum_type;

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

What are cons/pros of these two methods? 这两种方法的缺点是什么? Is there a better one? 还有更好的吗?

The only advantage with the former is that it's backwards-compatible with ancient C standards. 前者的唯一优势是它与古老的C标准向后兼容。

Apart from that, the latter alternative is superior, as it ensures data integrity even if the enum is modified or items change places. 除此之外,后一种选择是优越的,因为它确保数据完整性,即使枚举被修改或项目改变位置。 However, it should be completed with a check to ensure that the number of items in the enum corresponds with the number of items in the look-up table: 但是,它应该通过检查来完成,以确保枚举中的项目数与查找表中的项目数相对应:

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

The above is the best method for a simple "enum - lookup table" coupling. 以上是简单的“枚举 - 查找表”耦合的最佳方法。 Another option would be to use structs, but that's more suitable for more complex data types. 另一种选择是使用结构,但这更适合更复杂的数据类型。


And finally, more as a side-note, the 3rd version would be to use "X macros". 最后,更多的是作为旁注,第3版将是使用“X宏”。 This is not recommended unless you have specialized requirements regarding code repetition and maintenance. 除非您对代码重复和维护有特殊要求,否则不建议这样做。 I'll include it here for completeness, but I don't recommend it in the general case: 为了完整起见,我会将其包含在此处,但我不建议在一般情况下使用它:

#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");

Another possibility might be to use a function, instead of an array: 另一种可能是使用函数而不是数组:

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

gcc, at least, will warn if you add an enum value but forget to add the matching switch case. 至少gcc会在添加枚举值时发出警告,但忘记添加匹配的switch case。

(I suppose you could try making this sort of function inline , as well.) (我想你也可以尝试inline这种功能。)


Addendum: The gcc warning I mentioned applies only if the switch statement does not have a default case. 附录:我提到的海湾合作委员会警告仅当switch语句没有一个default情况下。 So if you want to print something for out-of-bounds values that somehow creep through, you could do that, not with a default case, but with something like this: 因此,如果你想打印一些以某种方式蠕变的越界值的东西,你可以这样做,而不是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)";
}

It's also nice to include the out-of-bounds value: 包含越界值也很好:

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

(This last fragment has a couple of additional limitations, but this addendum is getting long already, so I won't belabor the point with them just now.) (这最后一个片段有一些额外的限制,但是这个附录已经很久了,所以我现在不会对它们提出异议。)

Another possibility is to user #defines . 另一种可能性是用户#defines

In spite of the many cons of its use, the main benefit is that #defines take up no space unless they are used... 尽管使用了许多缺点,但主要的好处是#defines不占用空间,除非它们被使用......

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

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

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