MicroVirus
MicroVirus

Reputation: 5477

Using a data buffer without violating strict aliasing

I want to rewrite a piece of (old) code to be standards compliant. The old code uses a buffer to store a POD-struct and a checksum to send it over the network and receive it. For sending, the code looks like:

struct MessageStruct {int a; float b;};

char buffer[sizeof(MessageStruct) + sizeof(uint32_t)];
((MessageStruct*)buffer)->a = 12;
((MessageStruct*)buffer)->b = 3.14159f;
*((uint32_t*)(buffer + sizeof(MessageStruct))) = 9876;

// Use the data buffer in some way.
SendMessage(buffer, sizeof(buffer));

For receiving, the code looks like:

struct MessageStruct {int a; float b;};

// Receive: char *buffer, int size
const MessageStruct *message = (MessageStruct*)buffer;
uint32_t checksum = *((uint32_t*)(buffer + sizeof(MessageStruct)));

How do I update this code to make it fully standards complaint, in particular not in violation of the strict aliasing rule?

I've found posts addressing similar issues: strict aliasing and alignment , Shared memory buffers in C++ without violating strict aliasing rules . However, none of these really answer my question; or maybe they do, but then I don't see it.

Update: As some of the answers have already stated, the easiest way is to use memcpy. I am wondering, is there any way to do this using placement new, or another construct that negates the need for copying and constructs it in-place?

Upvotes: 0

Views: 298

Answers (4)

Jarod42
Jarod42

Reputation: 217398

The Send part is easiest:

struct MessageStruct {int a; float b;};
struct MessageStructWithCheckSum { MessageStruct s; uint32_t check_sum;};

MessageStructWithCheckSum m;
m.s.a = 12;
m.s.b = 3.14159f;
m.check_sum = 9876;

// Use the data buffer in some way.
SendMessage(reinterpret_cast<const unsigned char*>(m), sizeof(m));

You can access the stored value of an object through [..] a char or unsigned char type.

For the read part, I think that you have to copy to avoid strict aliasing rules.

But in fact, you have to do special treatment to manage endianness (and types with representation which can be different), which is sort of copy anyway.

Upvotes: 1

Ulrich Eckhardt
Ulrich Eckhardt

Reputation: 17415

Firstly, your very approach of sending the in-memory representation of a struct to a different machine is flawed, because the other machine might have a different layout or even size than yours. Use a properly defined metaformat like JSON, XML or something like that.

Now, if you just don't care about such flaws, you can take your own approach one step further:

struct MessageStructWithChecksum:
    MessageStruct
{
    uint32_t checksum;
};

Apart from the conversions between struct and raw bytes, no aliasing questions. Also note that you can define function-local structures and that you can write a template for use with different message types.

Upvotes: 1

jrok
jrok

Reputation: 55395

Use memcpy:

MessageStruct msg;
msg.a = 12;
msg.b = 3.14;
uint32_t n = 9876;

memcpy(buffer, &msg, sizeof(msg));
memcpy(buffer + sizeof(msg), &n, sizeof(n));

Upvotes: 1

SergeyA
SergeyA

Reputation: 62583

In your case, the easiest way to avoid the violation of strict aliasing rule is to memcpy() stuff into your buffer.

Upvotes: 2

Related Questions