Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 . 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". 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). 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:

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.

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.

I have looked at implementing reflection such as How can I add reflection to a C++ application? and Ponder Library, but both approaches requires you to explicitly write out each item that can be reflected upon. In that case, I might as well just create a std::map<string, float> and iterate in a for loop via string/integer concatenation:

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? Your help is appreciated!

Again, I repeat - I absolutely cannot use an array/vector of any sort.

like image 998
dberm22 Avatar asked May 18 '16 22:05

dberm22


2 Answers

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. 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. 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.

like image 54
Andrew Avatar answered Sep 20 '22 10:09

Andrew


You can use type punning to treat the structure as an array.

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];
}
like image 39
Barmar Avatar answered Sep 20 '22 10:09

Barmar