关于C#:如何使用宏来收集变量名?

How can I use a macro for collecting variable names?

我想简化以下内容

1
2
3
4
5
6
7
8
class A {
    int a;
    int b;
    int c;
    std::vector<int*> addrs;
public:
    A() : addrs{ &a, &b, &c } {}
};

,所以我没有在两个地方写字段列表,即addrs的声明和初始化程序。是否有某种方法可以使用宏来收集声明并在以后使用它们。例如,

1
2
3
4
5
6
7
8
class A {
    VAR_DECL(a);
    VAR_DECL(b);
    VAR_DECL(c);
    std::vector<int*> addrs;
public:
    A() : addrs{ VAR_ADDRESSES } {}
};

对于上下文,这旨在实现某种属性自省系统。


您可以使用Boost预处理程序来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define VARIABLES (a)(b)(c)

#define DECLARE_MEMBER(maR, maType, maId) \\
  maType maId;

#define TAKE_ADDRESS(maR, maUnused, maIndex, maId) \\
  BOOST_PP_COMMA_IF(maIndex) & maId

class A {
  BOOST_PP_SEQ_FOR_EACH(DECLARE_MEMBER, int, VARIABLES)
  std::vector<int*> addrs;
public:
  A() : addrs { BOOST_PP_SEQ_FOR_EACH_I(TAKE_ADDRESS, %%, VARIABLES) } {}
};

// Now you can clean up:
#undef DECLARE_MEMBER
#undef TAKE_ADDRESS
// In case you no longer need the list, also:
#undef VARIABLES


我通常不回答"不要这样做,您真的想这样做"。但是在这种情况下,问题太明显了。

  • 您正在堆上分配内存,以获取编译时可用的信息。太恐怖了

  • 您的实现不必要地破坏了默认的复制并移动了构造函数的行为。希望您知道这一点。我希望每个重用该代码的人都知道这一点。

  • 我想您正在尝试实现的一种访问所有成员的通用方式。请执行以下操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class A {
        int a;
        int b;
        int c;

    public:
        A() {}

        template<class F> ForEachMember(F f) {
            f(a);
            f(b);
            f(c);
        }
    };
  • 如果F::operator()重载,这将支持不同类型的成员。

    如果这是代码中的常见模式,并且您认为重复成员名称容易出错,则可以使用boost::tupleboost::fusion

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <boost/fusion/algorithm/iteration/for_each.hpp>
    #include <boost/fusion/include/for_each.hpp>
    #include <boost/fusion/adapted/boost_tuple.hpp>
    #include <boost/fusion/include/boost_tuple.hpp>

    class A : boost::tuple<int, int, int> {
        template<class F> ForEachMember(F f) {
           boost::fusion::for_each( *this, f );
        }

        // if necessary, write getter/setter with pretty names
        int& a() { return get<0>(); }
    };


    您可以使用union:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class A {
        A() {
            static_assert(&u.a == &u.vars[0],"&u.a == &u.vars[0] failed");
            static_assert(&u.b == &u.vars[1],"&u.b == &u.vars[1] failed");
            static_assert(&u.c == &u.vars[2],"&u.c == &u.vars[2] failed");
        }
    private:
        union {
            struct {
                int a;
                int b;
                int c;
            };
            int vars[3];
        } u;
    };

    您可以消除地址向量并遍历成员(尽管我将其保留在此处)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    #include <iostream>
    #include <tuple>
    #include <vector>

    // Dedicated function

    template <typename T, std::size_t I = 0, typename ...Tuple>
    inline typename std::enable_if<I == sizeof...(Tuple), void>::type
    collect_addresses(std::vector<T*>& result, std::tuple<Tuple...>&) {
    }

    template <typename T, std::size_t I = 0, typename ...Tuple>
    inline typename std::enable_if<I < sizeof...(Tuple), void>::type
    collect_addresses(std::vector<T*>& result, std::tuple<Tuple...>& tuple) {
        result.push_back(&std::get(tuple));
        collect_addresses<T, I + 1, Tuple...>(result, tuple);
    }

    template <typename T, typename ...Tuple>
    inline std::vector<T*> collect_addresses(std::tuple<Tuple...>& tuple) {
        std::vector<T*> result;
        result.reserve(sizeof...(Tuple));
        collect_addresses(result, tuple);
        return result;
    }


    // Static function [Tuple]

    template <typename Function, std::size_t I = 0, typename ...Tuple>
    inline typename std::enable_if<I == sizeof...(Tuple), void>::type
    invoke_tuple(const Function&, std::tuple<Tuple...>&) {
    }

    template <typename Function, std::size_t I = 0, typename ...Tuple>
    inline typename std::enable_if<I < sizeof...(Tuple), void>::type
    invoke_tuple(const Function& function, std::tuple<Tuple...>& tuple) {
        function(std::get(tuple));
        invoke_tuple<Function, I + 1, Tuple...>(function, tuple);
    }


    // Member function [Tuple]

    template <typename Instance, typename Function, std::size_t I = 0, typename ...Tuple>
    inline typename std::enable_if<I == sizeof...(Tuple), void>::type
    invoke_tuple(Instance&, const Function&, std::tuple<Tuple...>&) {
    }

    template <typename Instance, typename Function, std::size_t I = 0, typename ...Tuple>
    inline typename std::enable_if<I < sizeof...(Tuple), void>::type
    invoke_tuple(Instance& instance, const Function& function, std::tuple<Tuple...>& tuple) {
        (instance.*function)(std::get(tuple));
        invoke_tuple<Instance, Function, I + 1, Tuple...>(instance, function, tuple);
    }



    // Static function [Variadic Template]

    template <typename Function>
    inline void invoke(const Function&) {
    }

    template <typename Function, typename T, typename ...Args>
    inline void invoke(const Function& function, T& value, Args&... args) {
        function(value);
        invoke(function, args...);
    }


    // Member function [Variadic Template]

    template <typename Instance, typename Function>
    inline void invoke(Instance&, const Function&) {
    }

    template <typename Instance, typename Function, typename T, typename ...Args>
    inline void invoke(Instance& instance, const Function& function, T& value, Args&... args) {
        (instance.*function)(value);
        invoke(instance, function, args...);
    }



    class A {
        // public in this test
        public:
        std::tuple<int, int, int> params;
        std::vector<int*> addrs;
        A() : addrs(collect_addresses<int>(params))
        {}
    };

    class B {
        private:
        typedef std::tuple<int, int, int> Params;

        // public in this test
        public:
        Params params;
        std::vector<int*> addrs;
        B()
        {
            addrs.reserve(std::tuple_size<Params>::value);
            invoke_tuple([this](int& i) { addrs.push_back(&i); }, params);
        }
    };

    class C {
        // public in this test
        public:
        int a;
        int b;
        int c;
        std::vector<int*> addrs;
        C()
        {
            addrs.reserve(3);
            invoke([this](int& i) { addrs.push_back(&i); }, a, b, c);
        }
    };

    int main(){
        A a;
        for(int* p: a.addrs) std::cout << (const void*)p << std::endl;
        B b;
        for(int* p: b.addrs) std::cout << (const void*)p << std::endl;
        C c;
        for(int* p: c.addrs) std::cout << (const void*)p << std::endl;
    }