Let\'s say, I have an array of unsigned chars that represents a bunch of POD objects (e.g. either read from a socket or via mmap). Which types they represent and at what positi
Using a union allows to escape anti-aliasing rule. In fact that is what unions are for. So casting pointers to a union type from a type that is part of the union is explicitly allowed in C++ standard (Clause 3.10.10.6). Same thing is allowed in C standard (6.5.7).
Therefore depending on the other properties a conforming equivalent of your sample can be as follows.
union to_pod {
uint8_t buffer[100];
Pod1 pod1;
Pod1 pod2;
//...
};
uint8_t buffer[100]; //filled e.g. from network
switch(buffer[0]) {
case 0: process(reinterpret_cast<to_pod*>(buffer + 4)->pod1); break;
case 1: process(reinterpret_cast<to_pod*>(buffer + 8 + buffer[1]*4)->pod2); break;
//...
}
The most correct way is to create a (temporary) variable of the desired POD class, and to use memcpy()
to copy data from the buffer into that variable:
switch(buffer[0]) {
case 0: {
Pod1 var;
std::memcpy(&var, &buffer[4], sizeof var);
process(var);
break;
}
case 1: {
Pod2 var;
std::memcpy(&var, &buffer[8 + buffer[1] * 4], sizeof var);
process(var);
break;
}
//...
}
There main reason for doing this is because of alignment issues: the data in the buffer may not be aligned correctly for the POD type you are using. Making a copy eliminates this problem. It also allows you to keep using the variable even if the network buffer is no longer available.
Only if you are absolutely sure that the data is properly aligned can you use the first solution you gave.
(If you are reading in data from the network, you should always check that the data is valid first, and that you won't read outside of your buffer. For example with &buffer[8 + buffer[1] * 4]
, you should check that the start of that address plus the size of Pod2 does not exceed the buffer length. Luckily you are using uint8_t
, otherwise you'd also have to check that buffer[1]
is not negative.)