如何使用宏来收集变量名

How can I use a macro for collecting variable names?

本文关键字:变量名 何使用      更新时间:2023-10-16

我想简化以下

class A {
    int a;
    int b;
    int c;
    std::vector<int*> addrs;
public:
    A() : addrs{ &a, &b, &c } {}
};

这样我就不需要在两个地方写字段列表,即声明和addrs的初始值设定项。是否有某种方法可以使用宏来收集声明并在以后使用它们。例如,

class A {
    VAR_DECL(a);
    VAR_DECL(b);
    VAR_DECL(c);
    std::vector<int*> addrs;
public:
    A() : addrs{ VAR_ADDRESSES } {}
};

就上下文而言,这是为了实现某种属性内省系统。

您可以使用Boost预处理器来完成此操作。

#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. 我想你试图实现的是访问所有成员的通用方式。执行以下操作:

    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:

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

您可以消除地址向量并迭代成员(不过我在这里保留了该向量)

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

您可以使用union:

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