2

我想简化以下

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

4 回答 4

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
于 2013-08-28T09:16:32.537 回答
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);
        }
    };
    

F::operator()如果重载,这支持不同类型的成员。

如果这是您代码中的常见模式,并且您认为重复成员名称容易出错,则可以使用boost::tupleand boost::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>(); }
};
于 2013-08-28T10:15:08.893 回答
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;
}
于 2013-08-28T12:30:35.990 回答
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;
};
于 2013-08-28T13:54:45.487 回答