Is there a simple way to convert C++ enum to string?

后端 未结 30 2598
我在风中等你
我在风中等你 2020-11-22 10:37

Suppose we have some named enums:

enum MyEnum {
      FOO,
      BAR = 0x50
};

What I googled for is a script (any language) that scans all

30条回答
  •  一个人的身影
    2020-11-22 11:12

    I just re-invented this wheel today, and thought I'd share it.

    This implementation does not require any changes to the code that defines the constants, which can be enumerations or #defines or anything else that devolves to an integer - in my case I had symbols defined in terms of other symbols. It also works well with sparse values. It even allows multiple names for the same value, returning the first one always. The only downside is that it requires you to make a table of the constants, which might become out-of-date as new ones are added for example.

    struct IdAndName
    {
       int          id;
       const char * name;
       bool operator<(const IdAndName &rhs) const { return id < rhs.id; }
    };
    #define ID_AND_NAME(x) { x, #x }
    
    const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end)
    {
       if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id)
          std::stable_sort(table_begin, table_end);
    
       IdAndName searchee = { id, NULL };
       IdAndName *p = std::lower_bound(table_begin, table_end, searchee);
       return (p == table_end || p->id != id) ? NULL : p->name;
    }
    
    template
    const char * IdToName(int id, IdAndName (&table)[N])
    {
       return IdToName(id, &table[0], &table[N]);
    }
    

    An example of how you'd use it:

    static IdAndName WindowsErrorTable[] =
    {
       ID_AND_NAME(INT_MAX),               // flag value to indicate unsorted table
       ID_AND_NAME(NO_ERROR),
       ID_AND_NAME(ERROR_INVALID_FUNCTION),
       ID_AND_NAME(ERROR_FILE_NOT_FOUND),
       ID_AND_NAME(ERROR_PATH_NOT_FOUND),
       ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES),
       ID_AND_NAME(ERROR_ACCESS_DENIED),
       ID_AND_NAME(ERROR_INVALID_HANDLE),
       ID_AND_NAME(ERROR_ARENA_TRASHED),
       ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY),
       ID_AND_NAME(ERROR_INVALID_BLOCK),
       ID_AND_NAME(ERROR_BAD_ENVIRONMENT),
       ID_AND_NAME(ERROR_BAD_FORMAT),
       ID_AND_NAME(ERROR_INVALID_ACCESS),
       ID_AND_NAME(ERROR_INVALID_DATA),
       ID_AND_NAME(ERROR_INVALID_DRIVE),
       ID_AND_NAME(ERROR_CURRENT_DIRECTORY),
       ID_AND_NAME(ERROR_NOT_SAME_DEVICE),
       ID_AND_NAME(ERROR_NO_MORE_FILES)
    };
    
    const char * error_name = IdToName(GetLastError(), WindowsErrorTable);
    

    The IdToName function relies on std::lower_bound to do quick lookups, which requires the table to be sorted. If the first two entries in the table are out of order, the function will sort it automatically.

    Edit: A comment made me think of another way of using the same principle. A macro simplifies the generation of a big switch statement.

    #define ID_AND_NAME(x) case x: return #x
    
    const char * WindowsErrorToName(int id)
    {
        switch(id)
        {
            ID_AND_NAME(ERROR_INVALID_FUNCTION);
            ID_AND_NAME(ERROR_FILE_NOT_FOUND);
            ID_AND_NAME(ERROR_PATH_NOT_FOUND);
            ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES);
            ID_AND_NAME(ERROR_ACCESS_DENIED);
            ID_AND_NAME(ERROR_INVALID_HANDLE);
            ID_AND_NAME(ERROR_ARENA_TRASHED);
            ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY);
            ID_AND_NAME(ERROR_INVALID_BLOCK);
            ID_AND_NAME(ERROR_BAD_ENVIRONMENT);
            ID_AND_NAME(ERROR_BAD_FORMAT);
            ID_AND_NAME(ERROR_INVALID_ACCESS);
            ID_AND_NAME(ERROR_INVALID_DATA);
            ID_AND_NAME(ERROR_INVALID_DRIVE);
            ID_AND_NAME(ERROR_CURRENT_DIRECTORY);
            ID_AND_NAME(ERROR_NOT_SAME_DEVICE);
            ID_AND_NAME(ERROR_NO_MORE_FILES);
            default: return NULL;
        }
    }
    

提交回复
热议问题