如何使用宏来收集变量名?

时间:2013-08-28 09:03:08

标签: c++ c++11 macros

我想简化以下

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

对于上下文,这是为了实现某种属性内省系统。

4 个答案:

答案 0 :(得分:6)

您可以使用Boost Preprocessor

#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)

我通常会避免“不要这样做,你真的想做那个而不是”答案。但在这种情况下,问题太明显了。

  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);
        }
    };
    
  4. 如果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>(); }
    };
    

答案 2 :(得分: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;
}

答案 3 :(得分:0)

您可以使用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;
};