簡體   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