I want to implement some function related to reflection in c++, but some issue occurred when I want to expand macro which be concatenated by two macro has been expanded.
I try reproducing this issue in the smallest code range. This is the issued code I reproduced below:
#include <iostream>
#define M_EXPAND(x) x
#define STRINGIZE_(x) #x
#define STRINGIZE(x) STRINGIZE_(x)
#define CONCAT_(left, right) left ## right
#define M_FUNC(...) std::cout << #__VA_ARGS__ << std::endl;
#define EXEC_ACTION_N(action, ...) action(__VA_ARGS__)
#define EXEC_ACTION(action, ...) CONCAT_(EXEC_ACTION_, N) (action, ##__VA_ARGS__)
#define EXEC_ACTION_NO_CONCAT(action, ...) EXEC_ACTION_##N (action, ##__VA_ARGS__)
#define EXEC_ACTION_NO_PASTING(action, ...) EXEC_ACTION_N(action, ##__VA_ARGS__)
#define CALL_FUNC(...) EXEC_ACTION(M_FUNC, ##__VA_ARGS__)
#define CALL_FUNC_NO_CONCAT(...) EXEC_ACTION_NO_CONCAT(M_FUNC, ##__VA_ARGS__)
#define CALL_FUNC_NO_PASTING(...) EXEC_ACTION_NO_PASTING(M_FUNC, ##__VA_ARGS__)
int main()
{
// output: CONCAT_(EXEC_ACTION_, N)(M_FUNC,foo, bar)
std::cout << STRINGIZE(M_EXPAND(CONCAT_(CALL_, FUNC(foo, bar)))) << std::endl;
// output: std::cout << "foo, bar" << std::endl;
std::cout << STRINGIZE(M_EXPAND(CONCAT_(CALL_, FUNC_NO_CONCAT(foo, bar)))) << std::endl;
// output: std::cout << "foo, bar" << std::endl;
std::cout << STRINGIZE(M_EXPAND(CONCAT_(CALL_, FUNC_NO_PASTING(foo, bar)))) << std::endl;
// output: std::cout << "foo, bar" << std::endl;
std::cout << STRINGIZE(CALL_FUNC(foo, bar)) << std::endl;
}
My goal was to get this macro
M_EXPAND(CONCAT_(CALL_, FUNC(foo, bar)))
to expand correctly like this std::cout << "foo, bar" << std::endl;
, but it actually expanded as this CONCAT_(EXEC_ACTION_, N)(M_FUNC,foo, bar)
, I don't know why that's happening.
I don't know why the C preprocessor can't expand/invocate several macro which is be concatenated by wrapping macro contains token pasting operator when the chain of invocation of macro is too long.
I test this issue that it works fine when only one macro which is be concatenated by macro CONCAT_
is called:
// this macro doesn't use macro CONCAT_
#define EXEC_ACTION_NO_CONCAT(action, ...) EXEC_ACTION_##N (action, ##__VA_ARGS__)
#define CALL_FUNC_NO_CONCAT(...) EXEC_ACTION_NO_CONCAT(M_FUNC, ##__VA_ARGS__)
// output: std::cout << "foo, bar" << std::endl;
std::cout << STRINGIZE(M_EXPAND(CONCAT_(CALL_, FUNC_NO_CONCAT(foo, bar)))) << std::endl;
besides, it also works fine when I doesn't use token pasting operator:
// this macro doesn't use token pasting operator
#define EXEC_ACTION_NO_PASTING(action, ...) EXEC_ACTION_N(action, ##__VA_ARGS__)
#define CALL_FUNC_NO_PASTING(...) EXEC_ACTION_NO_PASTING(M_FUNC, ##__VA_ARGS__)
// output: std::cout << "foo, bar" << std::endl;
std::cout << STRINGIZE(M_EXPAND(CONCAT_(CALL_, FUNC_NO_PASTING(foo, bar)))) << std::endl;
My test environment is
os: Win10 X64 professional
compiler: (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders) 13.1.0
compile cli: `g++ D:\xxx\bug_test.cc -o D:\xxx\bin\bug_test.exe -g -Wall -static-libgcc -std=gnu++17`
The solution I'd like is to be able to get the macro M_EXPAND(CONCAT_(CALL_, FUNC(foo, bar)))
to expand correctly like this std::cout << "foo, bar" << std::endl;
without removing related to the wrapping macro CONCAT_
, because the reflection funcion I want to implement need the usage of token pasting operator.
Can someone help me solve this problem? I would appreciate this!
--------------this is the whole code of which I want to implement.--------------
my_reflection.h
#ifndef MY_REFLECTION_H_
#define MY_REFLECTION_H_
#include <string>
#include <tuple>
#define M_EXPAND(x) x
#define NUMBER_OF_ARGS_(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, value, ...) value
#ifdef _MSC_VER // Microsoft compilers
#define NUMBER_OF_ARGS_2(...) NUMBER_OF_(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define NOA_AUGMENTER(...) unused, __VA_ARGS__
#define NUMBER_OF_ARGS_AUGMENT(...) M_EXPAND(NUMBER_OF_ARGS_(__VA_ARGS__, 5, 4, 3, 2, 1, 0))
#define NUMBER_OF_ARGS(...) NUMBER_OF_ARGS_AUGMENT(NOA_AUGMENTER(__VA_ARGS__))
#else
#define NUMBER_OF_ARGS(...) NUMBER_OF_ARGS_(0, ##__VA_ARGS__, 10, 9, 8, 7,6, 5, 4, 3, 2, 1, 0)
#endif
#define INDEX_FOR_ELEMENT_1(...) 0
#define INDEX_FOR_ELEMENT_2_(_1, N, ...) N
#define INDEX_FOR_ELEMENT_2(...) INDEX_FOR_ELEMENT_2_(__VA_ARGS__ 0, 1)
#define INDEX_FOR_ELEMENT_3_(_1, _2, N, ...) N
#define INDEX_FOR_ELEMENT_3(...) INDEX_FOR_ELEMENT_3_(__VA_ARGS__ 0, 1, 2)
#define INDEX_FOR_ELEMENT_4_(_1, _2, _3, N, ...) N
#define INDEX_FOR_ELEMENT_4(...) INDEX_FOR_ELEMENT_4_(__VA_ARGS__ 0, 1, 2, 3)
#define INDEX_FOR_ELEMENT_5_(_1, _2, _3, _4, N, ...) N
#define INDEX_FOR_ELEMENT_5(...) INDEX_FOR_ELEMENT_5_(__VA_ARGS__ 0, 1, 2, 3, 4)
#define INDEX_FOR_ELEMENT_6_(_1, _2, _3, _4, _5, N, ...) N
#define INDEX_FOR_ELEMENT_6(...) INDEX_FOR_ELEMENT_6_(__VA_ARGS__ 0, 1, 2, 3, 4, 5)
#define INDEX_FOR_ELEMENT_7_(_1, _2, _3, _4, _5, _6, N, ...) N
#define INDEX_FOR_ELEMENT_7(...) INDEX_FOR_ELEMENT_7_(__VA_ARGS__ 0, 1, 2, 3, 4, 5, 6)
#define INDEX_FOR_ELEMENT_8_(_1, _2, _3, _4, _5, _6, _7, N, ...) N
#define INDEX_FOR_ELEMENT_8(...) INDEX_FOR_ELEMENT_8_(__VA_ARGS__ 0, 1, 2, 3, 4, 5, 6, 7)
#define INDEX_FOR_ELEMENT_9_(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define INDEX_FOR_ELEMENT_9(...) INDEX_FOR_ELEMENT_9_(__VA_ARGS__ 0, 1, 2, 3, 4, 5, 6, 7, 8)
#define INDEX_FOR_ELEMENT_10_(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N
#define INDEX_FOR_ELEMENT_10(...) INDEX_FOR_ELEMENT_10_(__VA_ARGS__ 0, 1, 2, 3, 5, 6, 7, 8, 9)
#define STRINGIZE_(x) #x
#define STRINGIZE(x) STRINGIZE_(x)
#define CONCAT_(left, right) left ## right
#define CONCAT(left, right) CONCAT_(left, right)
#define ACTION_FOR_EACH(action, extra_data, ...) CONCAT(ACTION_FOR_EACH_, NUMBER_OF_ARGS(__VA_ARGS__))(action, extra_data, CONCAT(INDEX_FOR_ELEMENT_, NUMBER_OF_ARGS(__VA_ARGS__)), ##__VA_ARGS__)
#define ACTION_FOR_EACH_0(...)
#define ACTION_FOR_EACH_1(action, extra_data, func_for_index, element) action(extra_data, func_for_index(), element)
#define ACTION_FOR_EACH_2(action, extra_data, func_for_index, element, ...) action(extra_data, func_for_index(__VA_ARGS__,), element) ACTION_FOR_EACH_1(action, extra_data, func_for_index, __VA_ARGS__)
#define ACTION_FOR_EACH_3(action, extra_data, func_for_index, element, ...) action(extra_data, func_for_index(__VA_ARGS__,), element) ACTION_FOR_EACH_2(action, extra_data, func_for_index, __VA_ARGS__)
#define ACTION_FOR_EACH_4(action, extra_data, func_for_index, element, ...) action(extra_data, func_for_index(__VA_ARGS__,), element) ACTION_FOR_EACH_3(action, extra_data, func_for_index, __VA_ARGS__)
#define ACTION_FOR_EACH_5(action, extra_data, func_for_index, element, ...) action(extra_data, func_for_index(__VA_ARGS__,), element) ACTION_FOR_EACH_4(action, extra_data, func_for_index, __VA_ARGS__)
#define ACTION_FOR_EACH_6(action, extra_data, func_for_index, element, ...) action(extra_data, func_for_index(__VA_ARGS__,), element) ACTION_FOR_EACH_5(action, extra_data, func_for_index, __VA_ARGS__)
#define ACTION_FOR_EACH_7(action, extra_data, func_for_index, element, ...) action(extra_data, func_for_index(__VA_ARGS__,), element) ACTION_FOR_EACH_6(action, extra_data, func_for_index, __VA_ARGS__)
#define ACTION_FOR_EACH_8(action, extra_data, func_for_index, element, ...) action(extra_data, func_for_index(__VA_ARGS__,), element) ACTION_FOR_EACH_7(action, extra_data, func_for_index, __VA_ARGS__)
#define ACTION_FOR_EACH_9(action, extra_data, func_for_index, element, ...) action(extra_data, func_for_index(__VA_ARGS__,), element) ACTION_FOR_EACH_8(action, extra_data, func_for_index, __VA_ARGS__)
#define ACTION_FOR_EACH_10(action, extra_data, func_for_index, element, ...) action(extra_data, func_for_index(__VA_ARGS__,), element) ACTION_FOR_EACH_9(action, extra_data, func_for_index, __VA_ARGS__)
template<class T>
class TypeInfo;
template<class T>
TypeInfo<T> getTypeInfo(T obj)
{
return obj;
}
template<class T>
TypeInfo<T> getTypeInfo()
{
return {};
}
template<class T1, class T2>
class MetaInfoInterface
{
protected:
T2 instance_;
public:
using Type = T1;
virtual std::string getName() = 0;
virtual T1 getValue(const T2& obj) = 0;
virtual T1 getValue() = 0;
};
template<class T, class ...MemberTypes>
class TypeInfoInterface: public MetaInfoInterface<T, T>
{
public:
T getValue(const T& obj) override
{
return obj;
}
T getValue() override
{
return this->instance_;
}
std::tuple<MemberTypes...> getMembers()
{
return std::make_tuple(MemberTypes(this->instance_)...);
}
};
namespace metadata {}
#define OPEN_NAMESPACE(space_name)\
namespace metadata { namespace space_name {
#define CLOSE_NAMESPACE }}
#define GENERATE_FIELD_INFO(unused, i, field_name)\
class FieldInfo__##field_name: public MetaInfoInterface<decltype(ClassType::field_name), ClassType> { public: \
std::string getName() override { return #field_name; }\
Type getValue(const ClassType& obj) override { return obj.field_name; }\
Type getValue() override { return (this->instance_).field_name; }\
FieldInfo__##field_name() = default;\
FieldInfo__##field_name(const ClassType& obj) { this->instance_ = obj; }};
#define GENERATE_FUNCTION_INFO(unused, i, function_name)
#define GEN_INFOS_field(...)\
ACTION_FOR_EACH(GENERATE_FIELD_INFO, unused, ##__VA_ARGS__)
#define GEN_INFOS_func(...)\
ACTION_FOR_EACH(GENERATE_FUNCTION_INFO, unused, ##__VA_ARGS__)
#define GEN_MEMBERS(unused, i, section)\
M_EXPAND(CONCAT(GEN_INFOS_, section))
#define GET_INFOS_TYPE_field(...)\
, FieldInfo__##field_name
#define GET_INFOS_TYPE_func(...)
#define GET_MEMBERS_TYPE(unused, i, section)\
M_EXPAND(CONCAT(GET_INFOS_TYPE_, section))
#define GENERATE_TYPE_INFO(class_type, ...) \
ACTION_FOR_EACH(GEN_MEMBERS, unused, ##__VA_ARGS__)\
template<> class TypeInfo<ClassType>: \
public TypeInfoInterface<ClassType ACTION_FOR_EACH(GET_MEMBERS_TYPE, unused, ##__VA_ARGS__)> { public:\
std::string getName() override { return #class_type; }\
TypeInfo() = default;\
TypeInfo(const ClassType& obj) { this->instance_ = obj; }};
#define REGISTER_CLASS(class_type, ...)\
OPEN_NAMESPACE(type_##class_type##_space)\
using ClassType = class_type;\
GENERATE_TYPE_INFO(class_type, ##__VA_ARGS__)\
CLOSE_NAMESPACE
#endif
my_reflection_test.cc
#include "my_reflection.h"
#include <iostream>
#include <vector>
#include <type_traits>
class Person
{
public:
std::string name_;
bool sex_;
unsigned birth_year_;
int age_;
float weight_;
};
int main()
{
// unexpected output: CONCAT_(ACTION_FOR_EACH_, 5)(GENERATE_FIELD_INFO, unused, CONCAT_(INDEX_FOR_ELEMENT_, 5),name_, sex_, age_, birth_year_, weight_)
std::cout << STRINGIZE(CONCAT_(GEN_INFOS_, field(name_, sex_, age_, birth_year_, weight_))) << std::endl;
/**
* expected output:
*
* (class FieldInfo__name_: public MetaInfoInterface<decltype(ClassType::name_), ClassType> { public: std::string getName() override { return "name_"; } Type getValue(const ClassType& obj) override { return obj.name_; } Type getValue() override { return (this->instance_).name_; } FieldInfo__name_() = default; FieldInfo__name_(const ClassType& obj) { this->instance_ = obj; }};
* class FieldInfo__sex_: public MetaInfoInterface<decltype(ClassType::sex_), ClassType> { public: std::string getName() override { return "sex_"; } Type getValue(const ClassType& obj) override { return obj.sex_; } Type getValue() override { return (this->instance_).sex_; } FieldInfo__sex_() = default; FieldInfo__sex_(const ClassType& obj) { this->instance_ = obj; }};
* class FieldInfo__age_: public MetaInfoInterface<decltype(ClassType::age_), ClassType> { public: std::string getName() override { return "age_"; } Type getValue(const ClassType& obj) override { return obj.age_; } Type getValue() override { return (this->instance_).age_; } FieldInfo__age_() = default; FieldInfo__age_(const ClassType& obj) { this->instance_ = obj; }};
* class FieldInfo__birth_year_: public MetaInfoInterface<decltype(ClassType::birth_year_), ClassType> { public: std::string getName() override { return "birth_year_"; } Type getValue(const ClassType& obj) override { return obj.birth_year_; } Type getValue() override { return (this->instance_).birth_year_; } FieldInfo__birth_year_() = default; FieldInfo__birth_year_(const ClassType& obj) { this->instance_ = obj; }};
* class FieldInfo__weight_: public MetaInfoInterface<decltype(ClassType::weight_), ClassType> { public: std::string getName() override { return "weight_"; } Type getValue(const ClassType& obj) override { return obj.weight_; } Type getValue() override { return (this->instance_).weight_; } FieldInfo__weight_() = default; FieldInfo__weight_(const ClassType& obj) { this->instance_ = obj; }};)
*/
std::cout << STRINGIZE((GEN_INFOS_field(name_, sex_, age_, birth_year_, weight_))) << std::endl;
/**
* unexpected output:
*
* (namespace metadata { namespace type_Person_space { using ClassType = Person;
* ACTION_FOR_EACH(GENERATE_FIELD_INFO, unused,name_, sex_, age_, birth_year_, weight_)
* ACTION_FOR_EACH(GENERATE_FUNCTION_INFO, unused,prints, getNum)
* template<> class TypeInfo<ClassType>: public TypeInfoInterface<ClassType , FieldInfo__field_name > { public: std::string getName() override { return "Person"; } TypeInfo() = default; TypeInfo(const ClassType& obj) { this->instance_ = obj; }}; }})
*
*/
std::cout << STRINGIZE((M_EXPAND(REGISTER_CLASS(
Person,
field(name_, sex_, age_, birth_year_, weight_),
func(prints, getNum)
)))) << std::endl;
}
As you can see from the above code, I want this macro GEN_INFOS_field
to be fully expanded correctly to generate all the meta information related to the class members. But strangely, the macro expansion stops halfway through, just remain the code CONCAT_(ACTION_FOR_EACH_, 5)(GENERATE_FIELD_INFO, unused, CONCAT_(INDEX_FOR_ELEMENT_, 5),name_, sex_, age_, birth_year_, weight_)
. So I'm trying to figure out why?
#define EXEC_ACTION(action, ...) CONCAT_(EXEC_ACTION_, N) (action, ##__VA_ARGS__) CONCAT_(CALL_, FUNC(foo, bar))
CONCAT_ is painted blue inside CONCAT_. The trivially simple fix is just to pick another macro name. See CONCAT2 below:
#define M_EXPAND(x) x
#define STRINGIZE_(x) #x
#define STRINGIZE(x) STRINGIZE_(x)
#define CONCAT_(left, right) left ## right
#define CONCAT2(left, right) left ## right
#define M_FUNC(...) std::cout << #__VA_ARGS__ << std::endl;
#define EXEC_ACTION_N(action, ...) action(__VA_ARGS__)
#define EXEC_ACTION(action, ...) CONCAT2(EXEC_ACTION_, N) (action, ##__VA_ARGS__)
#define EXEC_ACTION_NO_CONCAT(action, ...) EXEC_ACTION_##N (action, ##__VA_ARGS__)
#define EXEC_ACTION_NO_PASTING(action, ...) EXEC_ACTION_N(action, ##__VA_ARGS__)
#define CALL_FUNC(...) EXEC_ACTION(M_FUNC, ##__VA_ARGS__)
#define CALL_FUNC_NO_CONCAT(...) EXEC_ACTION_NO_CONCAT(M_FUNC, ##__VA_ARGS__)
#define CALL_FUNC_NO_PASTING(...) EXEC_ACTION_NO_PASTING(M_FUNC, ##__VA_ARGS__)
// expands to: "std::cout << \"foo, bar\" << std::endl;"
STRINGIZE(M_EXPAND(CONCAT_(CALL_, FUNC(foo, bar))))