宏中__VA_ARGS__与##__VA_ARGS__差异及结构体反射宏问题排查
结构体成员反射宏的问题分析与解决
问题描述
尝试实现C++结构体/类成员的简单反射,编写了模板和宏定义,但测试时宏展开结果不符合预期:
原代码实现
template <typename T> struct ReflectionTypeStringTraits { static const char* value; }; template <> struct ReflectionTypeStringTraits<int> { static const char* value; }; const char* ReflectionTypeStringTraits<int>::value = "int"; template <> struct ReflectionTypeStringTraits<std::string> { static const char* value; }; const char* ReflectionTypeStringTraits<std::string>::value = "string"; #define REFLECTION_MACRO_CALL_VEC(CALL1, CALL2, CALL3, CALL4, CALL, ...) CALL #define REFLECTION_TYPE_MEMBER_TYPE_MAP_0(struct_name) #define REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, member_name) { #member_name, ReflectionTypeStringTraits<decltype(struct_name::member_name)>::value }, #define REFLECTION_TYPE_MEMBER_TYPE_MAP_2(struct_name, member_name, ...) REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, member_name) REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, ##__VA_ARGS__) #define REFLECTION_TYPE_MEMBER_TYPE_MAP_3(struct_name, member_name, ...) REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, member_name) REFLECTION_TYPE_MEMBER_TYPE_MAP_2(struct_name, ##__VA_ARGS__) #define REFLECTION_TYPE_MEMBER_TYPE_MAP_4(struct_name, member_name, ...) REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, member_name) REFLECTION_TYPE_MEMBER_TYPE_MAP_3(struct_name, ##__VA_ARGS__) #define REFLECTION_TYPE_MEMBER_TYPE_MAP(struct_name, ...) \ REFLECTION_MACRO_CALL_VEC(__VA_ARGS__, \ REFLECTION_TYPE_MEMBER_TYPE_MAP_4, REFLECTION_TYPE_MEMBER_TYPE_MAP_3, REFLECTION_TYPE_MEMBER_TYPE_MAP_2, REFLECTION_TYPE_MEMBER_TYPE_MAP_1, \ REFLECTION_TYPE_MEMBER_TYPE_MAP_0)(struct_name, __VA_ARGS__) #define MAKE_REFLECTION(struct_name, ...) \ struct struct_name##_Reflection { \ struct_name##_Reflection(struct_name& obj) : this_(std::make_shared<struct_name>(obj)) {} \ std::shared_ptr<struct_name> this_; \ static std::map<std::string, std::string> fieldMap; \ }; \ std::map<std::string, std::string> struct_name##_Reflection::fieldMap = { \ REFLECTION_TYPE_MEMBER_TYPE_MAP(struct_name, __VA_ARGS__) \ } #define GET_REFLECTION(struct_name, obj) struct_name##_Reflection(obj)
测试代码
struct Test { int a; int b; std::string c; }; MAKE_REFLECTION(Test, a, b, c);
实际展开结果
struct Test_Reflection { Test_Reflection(Test& obj) : this_(std::make_shared<Test>(obj)) {} std::shared_ptr<Test> this_; static std::map<std::string, std::string> fieldMap; }; std::map<std::string, std::string> Test_Reflection::fieldMap = { { "a,b,c", ReflectionTypeStringTraits<decltype(Test::a,b,c)>::value }, };
期望展开结果
struct Test_Reflection { Test_Reflection(Test& obj) : this_(std::make_shared<Test>(obj)) {} std::shared_ptr<Test> this_; static std::map<std::string, std::string> fieldMap; }; std::map<std::string, std::string> Test_Reflection::fieldMap = { { "a", ReflectionTypeStringTraits<decltype(Test::a)>::value }, { "b", ReflectionTypeStringTraits<decltype(Test::b)>::value }, { "c", ReflectionTypeStringTraits<decltype(Test::c)>::value }, };
__VA_ARGS__与##__VA_ARGS__的差异
- VA_ARGS:代表宏定义中可变参数的内容。当可变参数为空时,会在宏展开后留下多余的逗号,导致编译错误;当传递多个参数时,参数会原样传递,但宏嵌套调用时可能因展开顺序问题,导致参数被错误合并为单个。
- ##VA_ARGS:C++11引入的扩展特性。当可变参数为空时,自动删除前面的逗号;当可变参数非空时,将__VA_ARGS__与前面内容连接,确保参数被正确拆分传递,避免多余逗号或参数合并问题。
问题解决
原问题的核心是REFLECTION_MACRO_CALL_VEC宏的参数顺序错误,无法根据可变参数数量正确选择对应的REFLECTION_TYPE_MEMBER_TYPE_MAP_N宏。修正参数计数逻辑即可解决:
修正后的宏定义
template <typename T> struct ReflectionTypeStringTraits { static const char* value; }; template <> struct ReflectionTypeStringTraits<int> { static const char* value; }; const char* ReflectionTypeStringTraits<int>::value = "int"; template <> struct ReflectionTypeStringTraits<std::string> { static const char* value; }; const char* ReflectionTypeStringTraits<std::string>::value = "string"; // 修正:根据参数数量选择对应的MAP宏 #define REFLECTION_SELECT_MAP(...) REFLECTION_SELECT_MAP_(__VA_ARGS__, 4, 3, 2, 1, 0) #define REFLECTION_SELECT_MAP_(a, b, c, d, e, N, ...) REFLECTION_TYPE_MEMBER_TYPE_MAP_##N #define REFLECTION_TYPE_MEMBER_TYPE_MAP_0(struct_name) #define REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, member_name) { #member_name, ReflectionTypeStringTraits<decltype(struct_name::member_name)>::value }, #define REFLECTION_TYPE_MEMBER_TYPE_MAP_2(struct_name, member_name, ...) REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, member_name) REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, ##__VA_ARGS__) #define REFLECTION_TYPE_MEMBER_TYPE_MAP_3(struct_name, member_name, ...) REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, member_name) REFLECTION_TYPE_MEMBER_TYPE_MAP_2(struct_name, ##__VA_ARGS__) #define REFLECTION_TYPE_MEMBER_TYPE_MAP_4(struct_name, member_name, ...) REFLECTION_TYPE_MEMBER_TYPE_MAP_1(struct_name, member_name) REFLECTION_TYPE_MEMBER_TYPE_MAP_3(struct_name, ##__VA_ARGS__) #define REFLECTION_TYPE_MEMBER_TYPE_MAP(struct_name, ...) \ REFLECTION_SELECT_MAP(__VA_ARGS__)(struct_name, __VA_ARGS__) #define MAKE_REFLECTION(struct_name, ...) \ struct struct_name##_Reflection { \ struct_name##_Reflection(struct_name& obj) : this_(std::make_shared<struct_name>(obj)) {} \ std::shared_ptr<struct_name> this_; \ static std::map<std::string, std::string> fieldMap; \ }; \ std::map<std::string, std::string> struct_name##_Reflection::fieldMap = { \ REFLECTION_TYPE_MEMBER_TYPE_MAP(struct_name, __VA_ARGS__) \ } #define GET_REFLECTION(struct_name, obj) struct_name##_Reflection(obj)
修正逻辑说明
REFLECTION_SELECT_MAP宏通过传递可变参数和数字列表,自动识别参数数量并返回对应的REFLECTION_TYPE_MEMBER_TYPE_MAP_N宏。- 当传入3个成员参数时,会正确选择
REFLECTION_TYPE_MEMBER_TYPE_MAP_3,展开后生成三个独立的map条目,符合预期。
内容的提问来源于stack exchange,提问作者kevin
相关产品推荐
相关产品推荐

