C++宏/元程序在编译时确定成员数量

发布于 2024-11-26 17:24:31 字数 456 浏览 1 评论 0原文

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

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

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

//eg:

class message_b
{
  long long identifier;
  char foobar;
}


bitset<message_b::count_members> thebits;

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

I am working on an application with a message based / asynchronous agent-like architecture.
There will be a few dozen distinct message types, each represented by C++ types.

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

Is it possible to write a macro/meta-program that would allow calculating the number of data members within the class at compile time?

//eg:

class message_b
{
  long long identifier;
  char foobar;
}


bitset<message_b::count_members> thebits;

I am not familiar with C++ meta programming, but could boost::mpl::vector allow me to accomplish this type of calculation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

铃予 2024-12-03 17:24:31

正如其他人已经建议的那样,您需要 Boost.Fusion< /a> 及其 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;

as others already suggested, you need Boost.Fusion and its BOOST_FUSION_DEFINE_STRUCT. You'll need to define your struct once using unused but simple syntax. As result you receive required count_members (usually named as size) and much more flexibility than just that.

Your examples:

Definition:

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

usage:

message_a a;
size_t count_members = message_a::size;
非要怀念 2024-12-03 17:24:31

不,C++ 中无法知道所有成员的名称或实际有多少个成员。

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

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

我没有测试它,因此可能存在错误。

No, there is no way in C++ to know the names of all members or how many members are actually there.

You could store all types in a mpl::vector along in your classes but then you face the problem of how to turn them into members with appropriate names (which you cannot achieve without some macro hackery).

Using std::tuple instead of PODs is a solution that generally works but makes for incredible messy code when you actually work with the tuple (no named variables) unless you convert it at some point or have a wrapper that forwards accessors onto the tuple member.

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

A solution with Boost.PP and 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;
}

I didn't test it so there could be bugs.

倾城°AllureLove 2024-12-03 17:24:31

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

对您问题的简短回答是直接使用 magic_get 中的设施,而不是自己重新实现它们。毕竟,即使查看 pre-Boost 版本的代码,它也不完全是清楚它是如何工作的。在评论中,有一次提到了一些关于构造函数参数的内容;我怀疑这是关键,因为它是可以计算常规函数的参数,因此它可能正在计算大括号初始化结构所需的参数数量。这表明它可能只能使用普通的旧结构而不是具有您自己的方法的对象。

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

There are several answers simply saying that it is not possible, and if you hadn't linked to magic_get I would've agreed with them. But magic_get shows, to my amazement, that it actually is possible in some cases. This goes to show that proving that something is not possible is harder than proving that something is possible!

The short answer to your question would be to use the facilities in magic_get directly rather than reimplement them yourself. After all, even looking at the pre-Boost version of the code, it's not exactly clear how it works. At one point in the comments it mentions something about constructor arguments; I suspect this is the key, because it is possible to count the arguments to a regular function, so perhaps it is counting the number of arguments needed to brace-initialise the struct. This indicates that it may only be possible with plain old structs rather than objects with your own methods.

Despite all this, I would suggest using a reflection library as others have suggested. A good one that I often recommend is Google's protobuf library, which has reflection and serialisation along with multi-language support. However, it is intended only for data-only objects (like plain old structs but with vectors and strings).

无远思近则忧 2024-12-03 17:24:31

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

Plain structs do not support counting members, but boost::fusion offers a good way to declare a struct that is count- and iteratable.

云朵有点甜 2024-12-03 17:24:31

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

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

Something like this might get you closer:

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;
};
水中月 2024-12-03 17:24:31

如果您的类型尊重某些属性 (< em>“SimpleAggregate”),您可以使用 magic_get (现在是 boost_pfr< /a>)(来自 C++14/C++17)。

所以你会得到类似的东西:

class message_b
{
public;
  long long identifier;
  char foobar;
};

static_assert(boost::pfr::tuple_size<message_b>::value == 2);

If your types respect some properties ("SimpleAggregate"), you might use magic_get (which is now boost_pfr) (from C++14/C++17).

So you will have something like:

class message_b
{
public;
  long long identifier;
  char foobar;
};

static_assert(boost::pfr::tuple_size<message_b>::value == 2);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文