Я только что изобрел это колесо сегодня и решил поделиться им.
Эта реализация не требует каких-либо изменений в коде, который определяет константы, которые могут быть перечислениями или #define
s или чем-то еще, что превращается в целое число - в моем случае я имел символы, определенные условия других символов. Это также хорошо работает с разреженными значениями. Он даже позволяет использовать несколько имен для одного и того же значения, всегда возвращая первое. Единственным недостатком является то, что вам необходимо составить таблицу констант, которая может устареть, например, при добавлении новых.
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<int N>
const char * IdToName(int id, IdAndName (&table)[N])
{
return IdToName(id, &table[0], &table[N]);
}
Пример того, как вы будете его использовать:
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);
Функция IdToName
полагается на std::lower_bound
для быстрого поиска, который требует сортировки таблицы. Если первые две записи в таблице не в порядке, функция сортирует их автоматически.
Редактировать: Комментарий заставил меня задуматься о другом способе использования того же принципа. Макрос упрощает генерацию большого оператора switch
.
#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;
}
}