This is my macro define,I want to make a simple reflection of the struct/class members
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)
This is my test
struct Test {
int a;
int b;
std::string c;
};
MAKE_REFLECTION(Test, a, b, c);
The macro is expand to
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 },
};
But what I want is
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 },
};
If I call REFLECTION_TYPE_MEMBER_TYPE_MAP_3 directly, It will get correct expand
REFLECTION_TYPE_MEMBER_TYPE_MAP_3(Test, a, b, c);
/* expand success
{ "a", ReflectionTypeStringTraits<decltype(Test::a)>::value },
{ "b", ReflectionTypeStringTraits<decltype(Test::b)>::value },
{ "c", ReflectionTypeStringTraits<decltype(Test::c)>::value }, ;
*/
If I remove the ## of VA_ARGS in REFLECTION_TYPE_MEMBER_TYPE_MAP_N, the macro parameter will become one
#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__)
// after change the ##__VA_ARGS__ to __VA_ARGS__
REFLECTION_TYPE_MEMBER_TYPE_MAP_3(Test, a, b, c);
/* only first parameter is correct
{ "a", ReflectionTypeStringTraits<decltype(Test::a)>::value },
{ "b, c", ReflectionTypeStringTraits<decltype(Test::b, c)>::value },
{ , ReflectionTypeStringTraits<decltype(Test::)>::value }, ;
*/
The default MSVC preprocessor is just buggy. Adding /Zc:preprocessor
to switch to the modern preprocessor fixes the issue.
GCC and Clang do the right thing without any flags in this case.
I don't think learning to accomodate the legacy preprocessor is worth one's time.
You can add following check to explicitly detect the legacy preprocessor and avoid the obscure errors if you forget to enable the modern one:
#if defined(_MSC_VER) && !defined(__clang__) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL == 1)
#error The standard-conformant MSVC preprocessor is required, enable it with `/Zc:preprocessor`.
#endif