简体   繁体   English

通过变量名迭代struct

[英]Iterate through struct by variable name

UPDATE: 6 months later, and I just came across this answer: Is it legal to index into a struct?: Answer by Slava . 更新:6个月后,我刚刚遇到了这样的答案: 索引结构是否合法?:Slava回答 I think this is a MUCH better solution than any of the ones provided here, as there is absolutely no undefined behavior. 我认为这是一个比这里提供的更好的解决方案,因为绝对没有未定义的行为。 Hopefully this helps the next person, since it is already too late for me to implement. 希望这对下一个人有所帮助,因为对我来说已经太晚了。


Before you comment telling me to use an array or vector, or any form of container instead, it is a hard truth that I cannot. 在您发表评论告诉我使用数组或向量或任何形式的容器之前,这是一个我不能做的事实。 I know, this would be solved with an array, and any solution otherwise is pretty "hacky". 我知道,这将通过数组来解决,否则任何解决方案都非常“hacky”。 I would love to use a container, but I absolutely cannot. 我很想用一个容器,但我绝对不能。

I am a mid-level developer at a very large corporation, and we are using a company-wide library for sending data over ethernet. 我是一家非常大的公司的中级开发人员,我们正在使用公司范围的库来通过以太网发送数据。 There are various reasons for why it cannot support arrays/vectors, and instead, uses structs of POD (Plain Old Data - chars, floats, ints, bools). 有多种原因可以解释为什么它不能支持数组/向量,而是使用POD结构(普通旧数据 - 字符,浮点数,整数,粗体)。 I start with an array of floats that I must use to fill a struct with the same number of floats. 我从一个浮点数开始,我必须使用它来填充具有相同浮点数的结构。 Since the purpose of this library is to send messages over ethernet, I only need to do the iteration twice - once on the send and one on the receive. 由于这个库的目的是通过以太网发送消息,我只需要进行两次迭代 - 一次在发送上,一次在接收上。 All other times, this data is stored as an array. 所有其他时间,此数据存储为数组。 I know - I should be serializing the arrays and sending them as is, but I repeat - I absolutely cannot. 我知道 - 我应该将数组序列化并按原样发送,但我再说一遍 - 我绝对不能。

I have a float[1024] , and must iterate through the array and fill the following struct: 我有一个float[1024] ,并且必须遍历数组并填充以下结构:

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

I am already generating this struct with BOOST_PP_REPEAT and BOOST_PP_SEQ_FOR_EACH_I so that I do not have to write out all 1024 floats, and it increases maintainability/extensibility. 我已经使用BOOST_PP_REPEATBOOST_PP_SEQ_FOR_EACH_I生成了这个结构,这样我就不必写出所有1024个浮点数,并且它增加了可维护性/可扩展性。

In the same fashion, I have tried iterating through the struct via pre-compiler ## concatination ( https://stackoverflow.com/a/29020943/2066079 ), but as this is done at pre-compiler time, it cannot be used for run-time getting/setting. 以同样的方式,我尝试通过预编译器## concatination( https://stackoverflow.com/a/29020943/2066079 )迭代结构,但由于这是在预编译时完成的,因此无法使用用于运行时获取/设置。

I have looked at implementing reflection such as How can I add reflection to a C++ application? 我已经研究过如何实现反射,例如如何向C ++应用程序添加反射? and Ponder Library , but both approaches requires you to explicitly write out each item that can be reflected upon. Ponder Library ,但这两种方法都要求您明确写出可以反映的每个项目。 In that case, I might as well just create a std::map<string, float> and iterate in a for loop via string/integer concatenation: 在这种情况下,我不妨创建一个std::map<string, float>并通过字符串/整数连接在for循环中迭代:

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

Can anyone recommend a cleaner solution that does not require me to write out in excess of 1024 lines of code? 任何人都可以推荐一个更清洁的解决方案,不需要我写出超过1024行代码? Your help is appreciated! 非常感谢您的帮助!

Again, I repeat - I absolutely cannot use an array/vector of any sort. 我再说一遍 - 我绝对不能使用任何类型的数组/向量。

This may well be easier than you expect. 这可能比您预期的要容易。 First, some caveats: 首先,一些警告:

  1. Arrays are guaranteed, by the standard, to be contiguous; 按标准,阵列保证是连续的; that is, there's no padding inserted between them, and the array itself is aligned with the alignment requirements of the element type. 也就是说,它们之间没有插入填充,并且数组本身与元素类型的对齐要求对齐。

  2. Structs have no such restrictions; 结构没有这样的限制; they can be subject to arbitrary padding. 它们可以受到任意填充。 However, a given implementation (at a given version) will do this the same way in all translation units (otherwise, how else could the same structure definition be used to pass data across translation units?). 但是,给定的实现(在给定版本中)将在所有翻译单元中以相同的方式执行此操作(否则,如何使用相同的结构定义来跨翻译单元传递数据?)。 The usual way this is done is fairly sane, especially when the struct contains only members of a single type. 通常的方法是相当理智,特别是当struct 包含单个类型的成员时。 For such a struct, the alignment usually matches the largest alignment of the members, and there's usually no padding because all members have the same alignment. 对于这样的结构,对齐通常匹配成员的最大对齐,并且通常没有填充,因为所有成员具有相同的对齐。

In your case, your array of 1024 floats and your struct with 1024 float members almost certainly have exactly the same memory layout. 在你的情况下,1024个浮点数的数组和1024个浮点数的结构几乎肯定具有完全相同的内存布局。 This is absolutely not guaranteed by the standard , but your compiler may document its struct layout rules, and you can always assert the sizes and alignments match in your unit tests (you do have unit tests, right?) 标准绝对不能保证这一点,但您的编译器可能会记录其结构布局规则,并且您始终可以在单元测试中断言大小和对齐匹配(您确实有单元测试,对吧?)

Given those caveats, you will almost certainly be able to simply reinterpret_cast (or memcpy ) between the two. 鉴于这些警告,您几乎肯定能够简单地在两者之间reinterpret_cast (或memcpy )。

You can use type punning to treat the structure as an array. 您可以使用类型punning将结构视为数组。

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

You can use preprocessor metaprogramming to create your array. 您可以使用预处理器元编程来创建数组。 You could do something like: 你可以这样做:

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

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

Will need to adjust ACCESSOR most likely. 最有可能需要调整ACCESSOR。 It might not be legal to use auto here either. 在这里使用auto可能也不合法。

Then you do: 然后你做:

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

etc... 等等...

No UB needed. 不需要UB。

Use Boost.Hana , if you can compile with recent versions of Clang or GCC (afaik, the only compilers supported.) 使用Boost.Hana ,如果你可以用最新版本的Clang或GCC编译(afaik,唯一支持的编译器。)

Allow me to quote the section Introspection from the tutorial: 请允许我引用教程中的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? 例如,你有没有想过检查一些未知类型是否有一个名为foo的成员? Or perhaps at some point you have needed to iterate on the members of a struct? 或者在某些时候你需要迭代结构的成员?

For introspecting user-defined types you do have to define the struct with Hana, but this is not much different than defining a struct otherwise: 对于内省用户定义的类型,您必须使用Hana定义结构,但这与定义结构没有太大区别:

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

And you should easily be able to modify whatever macros you have to generate your current struct, to generate this one instead. 您应该可以轻松地修改生成当前结构的任何宏,以生成此结构。

This adds a nested struct to pseudovector , which consists only of a static member function. 这为pseudovector添加了一个嵌套结构,它只包含一个静态成员函数。 It does not affect POD-ness, size, or data layout. 它不会影响POD,大小或数据布局。

Then you can iterate through it as in this example: 然后你可以像在这个例子中一样迭代它:

pseudovector pv;

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

Here, pair 's members are a hana compile-time string (the member name) and the value. 这里, pair的成员是hana编译时字符串(成员名称)和值。 If you'd rather have your lambda take two arguments (the name and the value), use hana::fuse : 如果你想让你的lambda采用两个参数(名称和值),请使用hana::fuse

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM