简体   繁体   中英

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.

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". 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.

(I suppose you could try making this sort of function inline , as well.)


Addendum: The gcc warning I mentioned applies only if the switch statement does not have a default case. 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:

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 .

In spite of the many cons of its use, the main benefit is that #defines take up no space unless they are used...

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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