如何用C++宏实现带注册功能的自定义枚举类生成?
实现自定义枚举结构体的C++宏方案
核心问题解析
标准C++预处理器不允许在宏展开内部直接定义新宏——你之前在MY_ENUM_BEGIN里写的#define ENUM_ENTRY会被当作普通文本处理,无法生效。要实现需求,得用分层宏替换+临时宏占位的技巧,把ENUM_ENTRY的定义和结构体生成逻辑绑定。
完整实现代码
#include <map> #include <string> // 临时宏:定义枚举项、注册函数 #define MY_ENUM_TEMP_ENTRY(name, val, desc) \ static constexpr auto name = val; \ static void register_##name() { \ _map.insert({val, desc}); \ } // 清理临时宏,避免污染全局空间 #define MY_ENUM_CLEANUP() \ #undef ENUM_ENTRY // 启动宏:定义结构体框架,重定向ENUM_ENTRY到临时宏 #define MY_ENUM_BEGIN(StructName) \ struct StructName { \ static inline std::map<int, std::string> _map; \ static void register_all() { \ // 所有条目注册调用会在这里展开 \ } \ #define ENUM_ENTRY MY_ENUM_TEMP_ENTRY // 结束宏:闭合结构体,清理临时宏 #define MY_ENUM_END() \ }; \ MY_ENUM_CLEANUP() // 使用示例 MY_ENUM_BEGIN(fun) ENUM_ENTRY(item1, 1, "item 1") ENUM_ENTRY(item2, 2, "item 2") ENUM_ENTRY(item3, 3, "item 3") MY_ENUM_END() // 测试代码 #include <iostream> int main() { fun::register_all(); for (auto& pair : fun::_map) { std::cout << pair.first << ": " << pair.second << std::endl; } std::cout << "item1的值:" << fun::item1 << std::endl; return 0; }
展开逻辑说明
MY_ENUM_BEGIN(fun)展开后:- 生成
struct fun框架,包含静态映射容器_map和空的批量注册函数register_all - 通过
#define ENUM_ENTRY MY_ENUM_TEMP_ENTRY,把后续的ENUM_ENTRY全部重定向到临时宏
- 生成
ENUM_ENTRY(item1,1,"item 1")被替换为:static constexpr auto item1 = 1; static void register_item1() { _map.insert({1, "item 1"}); }MY_ENUM_END()展开后:- 闭合结构体定义
- 调用
MY_ENUM_CLEANUP()取消ENUM_ENTRY的定义,防止影响其他代码
优化:自动生成批量注册逻辑
上面的示例需要手动在register_all里调用每个register_*函数,我们可以修改宏让注册逻辑自动生成:
// 修改临时宏和启动宏 #define MY_ENUM_TEMP_ENTRY(name, val, desc) \ static constexpr auto name = val; \ static void register_##name() { \ _map.insert({val, desc}); \ } #define MY_ENUM_BEGIN(StructName) \ struct StructName { \ static inline std::map<int, std::string> _map; \ static void register_all() { \ static bool _initialized = false; \ if (_initialized) return; \ _initialized = true; \ #define ENUM_ENTRY(name, val, desc) \ register_##name(); \ MY_ENUM_TEMP_ENTRY(name, val, desc) #define MY_ENUM_END() \ } \ }; \ MY_ENUM_CLEANUP()
修改后,register_all会自动包含所有条目注册函数的调用,无需手动添加。
注意事项
- 必须严格按
MY_ENUM_BEGIN→ENUM_ENTRY→MY_ENUM_END的顺序使用,否则宏展开会出错 - 临时宏
MY_ENUM_TEMP_ENTRY和MY_ENUM_CLEANUP不要在其他场景使用,避免冲突 - 如果需要支持非int类型的枚举键,直接修改
_map的模板参数和MY_ENUM_TEMP_ENTRY的参数即可
内容的提问来源于stack exchange,提问作者Mosklia Voidref
相关产品推荐
相关产品推荐

