为什么多次使用token粘贴操作符的包裹宏时,这个宏无法展开

问题描述 投票:0回答:1

我想在c++中实现一些与反射相关的功能,但是当我想扩展由两个宏连接而成的宏时,出现了一些问题。

我尝试在最小的代码范围内重现这个问题。 这是我在下面复制的已发布代码:

#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;
}

我的目标是得到这个宏

M_EXPAND(CONCAT_(CALL_, FUNC(foo, bar)))
像这样正确扩展
std::cout << "foo, bar" << std::endl;
,但它实际上扩展成这样
CONCAT_(EXEC_ACTION_, N)(M_FUNC,foo, bar)
,我不知道为什么会发生这种情况。

我不知道为什么当宏调用链太长时,C 预处理器无法展开/调用通过包装宏包含标记粘贴运算符连接的多个宏。

我测试了这个问题,当只有一个由宏

CONCAT_
连接的宏被调用时,它工作正常:

// 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;

此外,当我不使用令牌粘贴运算符时,它也可以正常工作:

// 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;

我的测试环境是

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`

我想要的解决方案是能够让宏

M_EXPAND(CONCAT_(CALL_, FUNC(foo, bar)))
像这样正确扩展
std::cout << "foo, bar" << std::endl;
而不删除与包装宏
CONCAT_
相关的内容,因为我想要实现的反射功能需要使用令牌粘贴运算符。

有人可以帮我解决这个问题吗?我将不胜感激!

--------------这是我要实现的全部代码。----------------

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;
}

从上面的代码中可以看到,我希望这个宏

GEN_INFOS_field
能够正确地完全展开,以生成与类成员相关的所有元信息。但奇怪的是,宏展开到一半就停止了,只保留了代码
CONCAT_(ACTION_FOR_EACH_, 5)(GENERATE_FIELD_INFO, unused, CONCAT_(INDEX_FOR_ELEMENT_, 5),name_, sex_, age_, birth_year_, weight_)
。所以我想弄清楚为什么?

c++ c-preprocessor variadic-macros token-pasting-operator
1个回答
0
投票
#define EXEC_ACTION(action, ...) CONCAT_(EXEC_ACTION_, N) (action, ##__VA_ARGS__)

CONCAT_(CALL_, FUNC(foo, bar))

CONCAT_ 在 CONCAT_ 内部被漆成蓝色。最简单的修复方法就是选择另一个宏名称。请参阅下面的 CONCAT2:

#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))))
© www.soinside.com 2019 - 2024. All rights reserved.