วันนี้ฉันเพิ่งประดิษฐ์วงล้อนี้ขึ้นมาใหม่และคิดว่าจะแบ่งปัน
การใช้งานนี้ไม่ต้องการการเปลี่ยนแปลงใด ๆ กับโค้ดที่กำหนดค่าคงที่ซึ่งอาจเป็นการแจงนับหรือ#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;
}
}