通过变量名遍历结构
Iterate through struct by variable name
更新:6 个月后,我刚刚看到这个答案:。我认为这是比此处提供的任何解决方案都好得多的解决方案,因为绝对没有未定义的行为。希望这对下一个人有帮助,因为我已经来不及实施了。
在你评论告诉我使用数组或向量,或任何形式的容器之前,这是一个残酷的事实,我不能。我知道,这可以用一个数组来解决,否则任何解决方案都很漂亮 "hacky"。我很想用容器,但我绝对不能。
我是一家大型公司的中层开发人员,我们正在使用公司范围的库通过以太网发送数据。它不支持 arrays/vectors 而是使用 POD 结构(普通旧数据 - 字符、浮点数、整数、布尔值)的原因有多种。我从一组浮点数开始,我必须用它来填充具有相同数量浮点数的结构。由于这个库的目的是通过以太网发送消息,我只需要进行两次迭代——一次发送,一次接收。所有其他时间,此数据存储为数组。我知道 - 我应该序列化数组并按原样发送它们,但我再说一遍 - 我绝对不能。
我有一个 float[1024]
,必须遍历数组并填充以下结构:
struct pseudovector
{
float data1;
float data2;
float data3;
...
float data1024;
}
我已经用 BOOST_PP_REPEAT and BOOST_PP_SEQ_FOR_EACH_I 生成了这个结构,这样我就不必写出所有 1024 个浮点数,它增加了 maintainability/extensibility.
以同样的方式,我尝试通过预编译器 ##
连接()遍历结构,但由于这是在预编译器时完成的,因此无法使用运行 时间 getting/setting.
我研究过实现反射,例如 How can I add reflection to a C++ application? and Ponder Library,但这两种方法都要求您明确写出可以反射的每个项目。在那种情况下,我不妨创建一个 std::map<string, float>
并通过 string/integer 连接在 for 循环中迭代:
for(i=0;i<1024;i++)
{
array[i] = map.get(std::string("data")+(i+1))
}
谁能推荐一个不需要我写出超过 1024 行代码的更简洁的解决方案?感谢您的帮助!
再一次,我再说一遍 - 我绝对不能使用任何类型的 array/vector。
这可能比您预期的要容易。首先,一些注意事项:
标准保证数组是连续的;也就是说,它们之间没有插入填充,并且数组本身与元素类型的对齐要求对齐。
结构没有这样的限制;它们可以进行任意填充。然而,给定的实现(在给定的版本)将在所有翻译单元中以相同的方式执行此操作(否则,如何使用相同的结构定义来跨翻译单元传递数据?)。这样做的通常方法是相当明智的,特别是当结构包含 only 单一类型的成员时。对于这样的结构,对齐方式 usually 匹配成员的最大对齐方式,并且 usually 没有填充,因为所有成员都具有相同的对齐方式。
在您的情况下,您的 1024 个浮点数数组和具有 1024 个浮点数成员的结构几乎可以肯定 具有完全相同的内存布局。 标准绝对不能保证这一点,但您的编译器可能会记录其结构布局规则,并且您始终可以在单元测试中断言大小和对齐方式匹配(您确实有单元测试,对吧?)
鉴于这些注意事项,您几乎可以肯定能够在两者之间简单地reinterpret_cast
(或memcpy
)。
您可以使用类型双关将结构视为数组。
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];
}
您可以使用预处理器元编程来创建数组。你可以这样做:
#define ACCESSOR(z, n, type) &type::data ## n
auto arr[] = {
BOOST_PP_ENUM(1000, ACCESSOR, pseudovector)
};
很可能需要调整 ACCESSOR。在这里使用 auto
也可能不合法。
然后你做:
auto val = (pv1.*arr)[4];
等...
不需要 UB。
如果您可以使用最新版本的 Clang 或 GCC 进行编译(afaik,唯一受支持的编译器。),请使用 Boost.Hana。
请允许我引用教程中的 Introspection 部分:
Static introspection, as we will discuss it here, is the ability of a
program to examine the type of an object at compile-time. In other
words, it is a programmatic interface to interact with types at
compile-time. For example, have you ever wanted to check whether some
unknown type has a member named foo? Or perhaps at some point you have
needed to iterate on the members of a struct?
对于 introspecting user-defined types 你必须用 Hana 定义结构,但这与用其他方式定义结构没有太大区别:
struct pseudovector {
BOOST_HANA_DEFINE_STRUCT(pseudovector,
(float, data1),
(float, data2),
…
);
};
而且您应该能够轻松地修改生成当前结构所需的任何宏,以生成这个结构。
这向 pseudovector
添加了一个嵌套结构,它只包含一个静态成员函数。它不影响 POD-ness、大小或数据布局。
然后你可以像这个例子一样遍历它:
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;
}));
更新:6 个月后,我刚刚看到这个答案:
在你评论告诉我使用数组或向量,或任何形式的容器之前,这是一个残酷的事实,我不能。我知道,这可以用一个数组来解决,否则任何解决方案都很漂亮 "hacky"。我很想用容器,但我绝对不能。
我是一家大型公司的中层开发人员,我们正在使用公司范围的库通过以太网发送数据。它不支持 arrays/vectors 而是使用 POD 结构(普通旧数据 - 字符、浮点数、整数、布尔值)的原因有多种。我从一组浮点数开始,我必须用它来填充具有相同数量浮点数的结构。由于这个库的目的是通过以太网发送消息,我只需要进行两次迭代——一次发送,一次接收。所有其他时间,此数据存储为数组。我知道 - 我应该序列化数组并按原样发送它们,但我再说一遍 - 我绝对不能。
我有一个 float[1024]
,必须遍历数组并填充以下结构:
struct pseudovector
{
float data1;
float data2;
float data3;
...
float data1024;
}
我已经用 BOOST_PP_REPEAT and BOOST_PP_SEQ_FOR_EACH_I 生成了这个结构,这样我就不必写出所有 1024 个浮点数,它增加了 maintainability/extensibility.
以同样的方式,我尝试通过预编译器 ##
连接()遍历结构,但由于这是在预编译器时完成的,因此无法使用运行 时间 getting/setting.
我研究过实现反射,例如 How can I add reflection to a C++ application? and Ponder Library,但这两种方法都要求您明确写出可以反射的每个项目。在那种情况下,我不妨创建一个 std::map<string, float>
并通过 string/integer 连接在 for 循环中迭代:
for(i=0;i<1024;i++)
{
array[i] = map.get(std::string("data")+(i+1))
}
谁能推荐一个不需要我写出超过 1024 行代码的更简洁的解决方案?感谢您的帮助!
再一次,我再说一遍 - 我绝对不能使用任何类型的 array/vector。
这可能比您预期的要容易。首先,一些注意事项:
标准保证数组是连续的;也就是说,它们之间没有插入填充,并且数组本身与元素类型的对齐要求对齐。
结构没有这样的限制;它们可以进行任意填充。然而,给定的实现(在给定的版本)将在所有翻译单元中以相同的方式执行此操作(否则,如何使用相同的结构定义来跨翻译单元传递数据?)。这样做的通常方法是相当明智的,特别是当结构包含 only 单一类型的成员时。对于这样的结构,对齐方式 usually 匹配成员的最大对齐方式,并且 usually 没有填充,因为所有成员都具有相同的对齐方式。
在您的情况下,您的 1024 个浮点数数组和具有 1024 个浮点数成员的结构几乎可以肯定 具有完全相同的内存布局。 标准绝对不能保证这一点,但您的编译器可能会记录其结构布局规则,并且您始终可以在单元测试中断言大小和对齐方式匹配(您确实有单元测试,对吧?)
鉴于这些注意事项,您几乎可以肯定能够在两者之间简单地reinterpret_cast
(或memcpy
)。
您可以使用类型双关将结构视为数组。
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];
}
您可以使用预处理器元编程来创建数组。你可以这样做:
#define ACCESSOR(z, n, type) &type::data ## n
auto arr[] = {
BOOST_PP_ENUM(1000, ACCESSOR, pseudovector)
};
很可能需要调整 ACCESSOR。在这里使用 auto
也可能不合法。
然后你做:
auto val = (pv1.*arr)[4];
等...
不需要 UB。
如果您可以使用最新版本的 Clang 或 GCC 进行编译(afaik,唯一受支持的编译器。),请使用 Boost.Hana。
请允许我引用教程中的 Introspection 部分:
Static introspection, as we will discuss it here, is the ability of a program to examine the type of an object at compile-time. In other words, it is a programmatic interface to interact with types at compile-time. For example, have you ever wanted to check whether some unknown type has a member named foo? Or perhaps at some point you have needed to iterate on the members of a struct?
对于 introspecting user-defined types 你必须用 Hana 定义结构,但这与用其他方式定义结构没有太大区别:
struct pseudovector {
BOOST_HANA_DEFINE_STRUCT(pseudovector,
(float, data1),
(float, data2),
…
);
};
而且您应该能够轻松地修改生成当前结构所需的任何宏,以生成这个结构。
这向 pseudovector
添加了一个嵌套结构,它只包含一个静态成员函数。它不影响 POD-ness、大小或数据布局。
然后你可以像这个例子一样遍历它:
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;
}));