【问题标题】:How can I use a macro for collecting variable names?如何使用宏来收集变量名?
【发布时间】:2013-08-28 09:03:07
【问题描述】:

我想简化以下内容

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

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

【问题讨论】:

  • 既然已经有了vector,为什么还需要分离的成员呢?你能自己摆脱变量吗?
  • @Nawaz 因为最好保留通常的成员变量语法,即不必说 *addrs[0] = 5;
  • 听起来您想要创建的是某种属性名称与其值的映射。如果是这样,您为什么决定不使用地图?
  • @Nawaz 虽然我想可以将成员变量声明为对向量的引用..., int& a 然后初始化为 a(vals[0]) 其中 vals 是向量 .
  • @Hulk 我可能会使用地图,但同样的问题仍然存在。

标签: c++ c++11 macros


【解决方案1】:

你可以使用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

【讨论】:

  • 非常好,完全解决了问题,也是有用的例子,即使我去重新设计。
【解决方案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::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>(); }
};

【讨论】:

  • 你仍然必须在两个地方写 a、b 和 c(或者可能有很多)并保持它们同步。这主要是我希望避免的。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    你可以使用联合:

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

    【讨论】:

      猜你喜欢
      • 2019-02-05
      • 1970-01-01
      • 1970-01-01
      • 2011-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多