【问题标题】:C++ macro/metaprogram to determine number of members at compile timeC++ 宏/元程序在编译时确定成员数
【发布时间】:2011-10-14 05:58:13
【问题描述】:

我正在开发一个具有基于消息/异步代理类架构的应用程序。 将有几十种不同的消息类型,每种都由 C++ 类型表示。

class message_a
{
  long long identifier;
  double some_value;
  class something_else;
  ...//many more data members
}

是否可以编写允许在编译时计算类中数据成员数量的宏/元程序?

//例如:

class message_b
{
  long long identifier;
  char foobar;
}


bitset<message_b::count_members> thebits;

我不熟悉 C++ 元编程,但是 boost::mpl::vector 可以让我完成这种类型的计算吗?

【问题讨论】:

  • 为什么不将std::tuple&lt;double, something_else&gt; data; 用作您的数据成员,而将std::tuple_size&lt;message_a::data&gt;::value 用作计数?
  • 我想通过调用代码中的名称而不是位置来访问成员。
  • 您总是可以将元组成员包装到访问器中,例如double &amp; some_value() { return std::get&lt;0&gt;(data); } 等...
  • 感谢大家的建议,我还没有决定前进的道路。但会跟进您的建议。
  • read my answer here。另外,here's 指向我的仓库的链接。 pod_reflection 库(仅 stl,不涉及 boost)可用于获取 pod 数据类型中的最大类数并获取类型的元组

标签: c++ macros metaprogramming


【解决方案1】:

不,C++ 中无法知道所有成员的名称或实际存在的成员数量。

您可以将所有类型存储在您的类中的 mpl::vector 中,但是您将面临如何将它们转换为具有适当名称的成员的问题(如果没有一些宏hacky,您将无法实现)。

使用std::tuple 代替 POD 是一种通常有效的解决方案,但当您实际使用元组(无命名变量)时,会产生令人难以置信的混乱代码,除非您在某个时候对其进行转换或有一个将访问器转发到元组成员。

class message {
public:
  // ctors
  const int& foo() const { return std::get<0>(data); }
  // continue boiler plate with const overloads etc

  static std::size_t nun_members() { return std::tuple_size<data>::value; }
private:
  std::tuple<int, long long, foo> data;
};

Boost.PP 和 MPL 的解决方案:

#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>
#include <boost/preprocessor.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>

struct Foo {
  typedef boost::mpl::vector<int, double, long long> types;

// corresponding type names here
#define SEQ (foo)(bar)(baz)
#define MACRO(r, data, i, elem) boost::mpl::at< types, boost::mpl::int_<i> >::type elem;
BOOST_PP_SEQ_FOR_EACH_I(MACRO, 0, SEQ)

};

int main() {
  Foo a;
  a.foo;
}

我没有测试它,所以可能会有错误。

【讨论】:

  • 一些宏骇客并没有被排除在外。实际上,我想做 std::tuple_size 正在做的事情。但我想避免访问器函数的所有样板。
  • 我会和 BOOST_PP 一起摆弄一些东西。您还应该看看 Boost.Fusion,尤其是 ADAPT_STRUCT。虽然这样你得到的比你讨价还价的要多。
【解决方案2】:

普通结构不支持计数成员,但 boost::fusion 提供了一种声明可计数和可迭代结构的好方法。

【讨论】:

  • 最有可能的是,boost 是通过多重继承实现的。如果是这样,那就不一样了。
  • 好吧,它也为普通结构提供了适配器宏,所以我假设它可以在普通结构上工作(可能是 MPL 在其上构建类型列表)并且确实如此。唉,除了阅读和询问之外没有办法知道:-)。
  • 是的,有可能获得成员字段的偏移量,然后您可以构建一个编译时偏移量列表。用一些疯狂的宏包装它可能会变魔术:)
【解决方案3】:

这样的事情可能会让你更接近:

struct Foo {
    Foo() : a(boost::get<0>(values)), b(boost::get<1>(values)) {}
    int &a;
    float &b;
    typedef boost::tuple<int,float> values_t;
    values_t values;
};

【讨论】:

    【解决方案4】:

    正如其他人已经建议的那样,您需要Boost.Fusion 及其BOOST_FUSION_DEFINE_STRUCT。您需要使用未使用但简单的语法定义一次结构。因此,您会收到所需的 count_members(通常命名为 size),并且比这更灵活。

    你的例子:

    定义:

    BOOST_FUSION_DEFINE_STRUCT(
        (), message_a,
        (long long, identifier),
        (double, some_value)
    )
    

    用法:

    message_a a;
    size_t count_members = message_a::size;
    

    【讨论】:

      【解决方案5】:

      有几个答案只是说这是不可能的,如果你没有链接到 magic_get 我会同意他们的。但令我惊讶的是,magic_get 表明,在某些情况下它实际上是可能的。这表明证明某事不可能比证明某事可能更难!

      对您的问题的简短回答是直接使用 magic_get 中的设施,而不是自己重新实现它们。毕竟,即使查看pre-Boost 版本的代码,也不清楚它是如何工作的。在 cmets 中的某一时刻,它提到了一些关于构造函数参数的内容;我怀疑这是关键,因为it is possible to count the arguments to a regular function,所以也许它正在计算大括号初始化结构所需的参数数量。这表明它可能只适用于普通的旧结构,而不是使用您自己的方法的对象。

      尽管如此,我还是建议像其他人建议的那样使用反射库。我经常推荐的一个好方法是 Google 的 protobuf 库,它具有反射和序列化以及多语言支持。但是,它仅适用于纯数据对象(例如普通的旧结构,但带有向量和字符串)。

      【讨论】:

        猜你喜欢
        • 2015-07-28
        • 1970-01-01
        • 1970-01-01
        • 2016-11-11
        • 1970-01-01
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多