如何通过预处理器实现枚举与大小写字符串的std::map映射
解决枚举与大小写字符串映射的预处理器+编译时方案
首先,我明白你想要的效果:通过一个CREATE宏一次性生成枚举值对应的大写字符串和小写字符串键值对,避免重复写冗余代码。你已经搞定了大写部分,现在卡在小写字符串的生成上——毕竟C++预处理器本身没有内置的字符串转小写功能,直接硬写肯定会出问题。
我给你推荐两种方案,优先推荐第一种(现代C++风格,简洁且扩展性强),第二种是纯预处理器方案(适合极端场景):
方案一:结合constexpr编译时转小写(推荐)
利用C++11及以上的constexpr函数,在编译时就把大写枚举名转成小写字符串,然后让CREATE宏同时生成两组键值对。这种方法不需要手动处理每个枚举名的字符,扩展性拉满。
完整代码示例
#include <map> #include <string> // 你的枚举类型 enum class Color { RED, BLUE, GREEN, YELLOW }; // 编译时将大写字符串转成小写的constexpr函数 constexpr const char* to_lower(const char* str) { static char buf[256] = {0}; // 足够容纳大部分枚举名的长度 int i = 0; while (str[i] != '\0') { // 大写字母转小写,其他字符保持不变 buf[i] = (str[i] >= 'A' && str[i] <= 'Z') ? (str[i] - 'A' + 'a') : str[i]; ++i; } buf[i] = '\0'; // 确保字符串结尾 return buf; } // 扩展后的CREATE宏,同时生成大写和小写键值对 #define CREATE(name) \ {#name, Color::name}, \ {std::string(to_lower(#name)), Color::name} // 初始化你的静态map const static std::map<std::string, Color> color_map = { CREATE(RED), CREATE(BLUE), CREATE(GREEN), CREATE(YELLOW) };
为什么可行?
#name会把枚举名字符串化(比如CREATE(RED)里的#name就是"RED")。to_lower函数在编译时就会把"RED"转换成"red",没有运行时开销。- 宏会自动展开成两组键值对,完美符合你的需求。
如果你的编译器支持C++20,还可以把constexpr换成consteval,强制函数在编译时执行,进一步保证性能。
方案二:纯预处理器方案(仅适合简单场景)
如果你必须完全用预处理器实现,那得手动为每个大写字母定义小写映射,还要拆分枚举名的每个字符——这种方法非常繁琐,只适合枚举名长度固定、数量很少的情况。
示例代码(以3字符和4字符枚举名为例)
#include <map> #include <string> enum class Color { RED, BLUE }; // 单个大写字母转小写的宏映射 #define LOWER_A 'a' #define LOWER_B 'b' #define LOWER_C 'c' #define LOWER_D 'd' #define LOWER_E 'e' #define LOWER_L 'l' #define LOWER_U 'u' #define TO_LOWER(c) LOWER_##c // 为每个枚举名拆分字符(必须手动写) #define NAME_FIRST_RED 'R' #define NAME_SECOND_RED 'E' #define NAME_THIRD_RED 'D' #define NAME_FIRST_BLUE 'B' #define NAME_SECOND_BLUE 'L' #define NAME_THIRD_BLUE 'U' #define NAME_FOURTH_BLUE 'E' // 处理3字符枚举名的宏 #define CREATE_3CHAR(name) \ {#name, Color::name}, \ { {TO_LOWER(NAME_FIRST_##name), TO_LOWER(NAME_SECOND_##name), TO_LOWER(NAME_THIRD_##name), '\0'}, Color::name } // 处理4字符枚举名的宏 #define CREATE_4CHAR(name) \ {#name, Color::name}, \ { {TO_LOWER(NAME_FIRST_##name), TO_LOWER(NAME_SECOND_##name), TO_LOWER(NAME_THIRD_##name), TO_LOWER(NAME_FOURTH_##name), '\0'}, Color::name }
显然,这种方案的扩展性极差,新增枚举名或者修改枚举名长度都要手动修改宏,所以除非你有特殊限制,否则优先用方案一。
为什么你之前的尝试失败?
C++预处理器没有内置的字符串转小写功能,直接尝试拼接小写字符串(比如硬写"red")会导致宏无法动态适配不同的枚举名;而如果尝试用预处理器的 token 拼接,又会因为没有字符转小写的内置逻辑而编译报错。方案一通过编译时函数绕开了预处理器的局限性,是更优雅的解决方式。
内容的提问来源于stack exchange,提问作者Schorsch
相关产品推荐
相关产品推荐

