The program - some sort of old-school network messaging:
// Common header for all network messages.
struct __attribute__((packed)) MsgHeader {
uint32_t msgType;
};
// One of network messages.
struct __attribute__((packed)) Msg1 {
MsgHeader header;
uint32_t field1;
};
// Network receive buffer.
uint8_t rxBuffer[MAX_MSG_SIZE];
// Receive handler. The received message is already in the rxBuffer.
void onRxMessage() {
// Detect message type
if ( ((const MsgHeader*)rxBuffer)->msgType == MESSAGE1 ) { // Breaks strict-aliasing!
// Process Msg1 message.
const Msg1* msg1 = (const Msg1*)rxBuffer;
if ( msg1->field1 == 0 ) { // Breaks strict-aliasing!
// Some code here;
}
return;
}
// Process other message types.
}
This code violates strict-aliasing in modern GCC (and falls down to unspecified behaviour in modern C++). What is the correct way to solve the problem (to make the code that doesn't throw the "strict-aliasing" warning)?
P.S. If rxBuffer is defined as:
union __attribute__((packed)) {
uint8_t[MAX_MSG_SIZE] rawData;
} rxBuffer;
and then I cast &rxBuffer to other pointers it doesn't cause any warnings. But is it safe, right and portable way?
Define rxBuffer
as a pointer to a union
of uint8_t[MAX_SIZE]
, MsgHeader
, Msg1
and whatever type you plan to cast to. Note that this would still break the strict aliasing rules, but in GCC it it guaranteed to work as non-standard extension.
EDIT: if such a method would lead to a too complicated declaration, a fully portable (if slower) way is to keep the buffer as a simple uint8_t[]
and memcpy
it to the opportune message struct as soon as it has to be reinterpreted. The feasability of this method obviously depends on your performance and efficiency needs.
EDIT 2: a third solution (if you are working on "normal" architectures) is to use Not valid because the conversion to the message type might not work, see herechar
or unsigned char
instead of uint8_t
. Such types are guaranteed to alias everything.
By working with the individual bytes, you can avoid all pointer casting and eliminate portability issues with endianness and alignment:
uint32_t decodeUInt32(uint8_t *p) {
// Decode big-endian, which is network byte order.
return (uint32_t(p[0])<<24) |
(uint32_t(p[1])<<16) |
(uint32_t(p[2])<< 8) |
(uint32_t(p[3]) );
}
void onRxMessage() {
// Detect message type
if ( decodeUInt32(rxBuffer) == MESSAGE1 ) {
// Process Msg1 message.
if ( decodeUInt32(rxBuffer+4) == 0 ) {
// Some code here;
}
return;
}
// Process other message types.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With