通过变量名迭代struct

时间:2016-05-18 22:17:42

标签: c++ reflection iterator iteration c-preprocessor

更新:6个月后,我刚刚看到了这个答案:Is it legal to index into a struct?: Answer by Slava。我认为这是一个比这里提供的更好的解决方案,因为绝对没有未定义的行为。希望这对下一个人有所帮助,因为对我来说已经太晚了。

在您评论告诉我使用数组或向量或任何形式的容器之前,这是一个我不能做的事实。我知道,这将通过一个数组来解决,否则任何解决方案都很糟糕" hacky"。我很想用一个容器,但我绝对不能。

我是一家大型公司的中级开发人员,我们正在使用公司范围的库通过以太网发送数据。有多种原因可以解释为什么它不能支持数组/向量,而是使用POD结构(普通旧数据 - 字符,浮点数,整数,粗体)。我从一个浮点数开始,我必须使用它来填充具有相同浮点数的结构。由于这个库的目的是通过以太网发送消息,我只需要进行两次迭代 - 一次在发送上,一次在接收上。所有其他时间,此数据存储为数组。我知道 - 我应该将数组序列化并按原样发送,但我再说一遍 - 我绝对不能。

我有float[1024],必须遍历数组并填充以下结构:

struct pseudovector
{
    float data1;
    float data2;
    float data3;
    ...
    float data1024;
}

我已经使用BOOST_PP_REPEATBOOST_PP_SEQ_FOR_EACH_I生成了这个结构,因此我不必写出所有1024个浮点数,并且它增加了可维护性/可扩展性。

以同样的方式,我尝试通过预编译器## concatination(https://stackoverflow.com/a/29020943/2066079)迭代结构,但是由于这是在预编译时完成的,所以它不能用于运行 - 获取/设置。

我已经考虑过实现How can I add reflection to a C++ application?Ponder Library之类的反射,但这两种方法都要求您明确写出可以反映的每个项目。在这种情况下,我不妨创建一个std::map<string, float>并通过字符串/整数连接迭代for循环:

for(i=0;i<1024;i++)
{
    array[i] = map.get(std::string("data")+(i+1))
}

有人可以推荐一种更清洁的解决方案,不需要我写出超过1024行代码吗?感谢您的帮助!

我再说一遍 - 我绝对不能使用任何类型的数组/向量。

4 个答案:

答案 0 :(得分:8)

这可能比您预期的要容易。首先,一些警告:

  1. 按照标准,阵列保证是连续的;也就是说,它们之间没有插入填充,并且数组本身与元素类型的对齐要求对齐。

  2. 结构没有这样的限制;它们可以受到任意填充。但是,给定的实现(在给定版本中)将在所有翻译单元中以相同的方式执行此操作(否则,如何使用相同的结构定义来跨翻译单元传递数据?)。通常的方法是相当合理,特别是当struct包含单个类型的成员时。对于这样的结构,对齐通常匹配成员的最大对齐,并且通常没有填充,因为所有成员具有相同的对齐。

  3. 在你的情况下,1024个浮点数的数组和1024个浮点数的结构几乎肯定具有完全相同的内存布局。这绝对不是标准保证的,但是您的编译器可能会记录其结构布局规则,并且您可以始终在单元测试中断言大小和对齐匹配(您确实有单元测试,对吗?)

    鉴于这些警告,您几乎肯定只能在两者之间reinterpret_cast(或memcpy)。

答案 1 :(得分:2)

您可以使用类型惩罚将结构视为数组。

float array[1024] = { ... };
pseudovector pv1;
float *f = static_cast<float*>(static_cast<void*>(&pv1));
for (int i = 0; i < 1024; i++) {
    f[i] = array[i];
}

答案 2 :(得分:1)

您可以使用预处理器元编程来创建数组。你可以这样做:

#define ACCESSOR(z, n, type) &type::data ## n

auto arr[] = {
    BOOST_PP_ENUM(1000, ACCESSOR, pseudovector)
};

最有可能需要调整ACCESSOR。在这里使用auto也可能不合法。

然后你做:

auto val = (pv1.*arr)[4];

等...

不需要UB。

答案 3 :(得分:1)

如果您可以使用Clang或GCC的最新版本(afaik,支持的唯一编译器)进行编译,请使用Boost.Hana

请允许我引用教程中的Introspection部分:

  

静态内省,正如我们将在这里讨论的那样,是一种能力   程序,用于在编译时检查对象的类型。其他   单词,它是一个与类型交互的编程接口   编译时间。例如,你有没有想过检查一下   未知类型有一个名为foo的成员?或者也许在某些时候你有   需要迭代结构的成员吗?

对于introspecting user-defined types,您必须使用Hana定义结构,但这与定义结构没有太大区别:

struct pseudovector {
   BOOST_HANA_DEFINE_STRUCT(pseudovector,
    (float, data1),
    (float, data2),
    …
  );
};

您应该可以轻松地修改生成当前结构所需的任何宏,而不是生成此结构。

这为pseudovector添加了一个嵌套结构,它只包含一个静态成员函数。它不会影响POD,大小或数据布局。

然后你可以像在这个例子中一样迭代它:

pseudovector pv;

hana::for_each(pv, [](auto pair) {
  std::cout << hana::to<char const*>(hana::first(pair)) << ": "
            << hana::second(pair) << std::endl;
});

这里,pair的成员是hana编译时字符串(成员名称)和值。如果你想让你的lambda采用两个参数(名称和值),请使用hana::fuse

hana::for_each(pv, hana::fuse([](auto name, auto member) {
  std::cout << hana::to<char const*>(name) << ": " << member << std::endl;
}));
相关问题