I was scrolling through some posts and I read about something called the strict aliasing rule. It looked awfully close to some code I've seen in a club project, relevant snippet below.
LibSerial::DataBuffer dataBuffer;
size_t BUFFER_SIZE = sizeof(WrappedPacket);
while(true) {
serial_port.Read(dataBuffer, sizeof(WrappedPacket));
uint8_t *rawDataBuffer = dataBuffer.data();
//this part
auto *wrappedPacket = (WrappedPacket *) rawDataBuffer;
...
the struct definitions are:
typedef struct __attribute__((__packed__)) TeensyData {
int16_t adc0, adc1, adc2, adc3, adc4, adc5, adc6, adc7, adc8, adc9, adc10, adc11;
int32_t loadCell0;
double tc0, tc1, tc2, tc3, tc4, tc5, tc6, tc7;
} TeensyData;
typedef struct __attribute__((__packed__)) WrappedPacket {
TeensyData dataPacket;
uint16_t packetCRC;
} WrappedPacket;
Hopefully it's pretty obvious that I'm new to C . So 1) is this a violation of the rule? and 2) if it is, what alternative solutions are there?
CodePudding user response:
Yeah, it's a violation. The rule lets you use raw byte access (char*
, unsigned char*
, std::byte*
) to access other data types. It doesn't let you use other data types to access arrays of raw bytes.
The solution is memcpy
:
WrappedPacket wpkt;
std::memcpy(&wpkt, dataBuffer.data(), sizeof wpkt);
CodePudding user response:
Yes, this is a strict aliasing violation and UB. But I wouldn't worry too much about it.
I wouldn't want to memcpy
a big struct solely for formal correctness, in hope that the compiler optimizes it out. But I would add std::launder
just in case1:
auto *wrappedPacket = std::launder(reinterpret_cast<WrappedPacket *>(rawDataBuffer));
Here's my reasoning:
.Read
most probably boils down to an opaque library call, so the compiler must optimize under the assumption that it does something that makes the code legal. For example, it could apply placement-new to the provided buffer, with the right type.
If we assume that it's the case, then std::launder
would "bless" the pointer to point to the said imaginary object, fixing the UB.
We, programmers, know that this assumption is false, but the compiler doesn't.
I'm not sure if C 20 implicit lifetimes relax the rules here and make launder
unnecessary. I would keep it.
1 launder
doesn't fix UB here. But it does reduce the probability of it blowing up in your face.