0

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?

Community
  • 1
  • 1
MicroVirus
  • 5,324
  • 2
  • 28
  • 53

4 Answers4

2

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

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Thanks for the swift answer. I was thinking about this solution as well, but I also was/am hoping for a solution that avoids copying. – MicroVirus Sep 25 '15 at 15:04
  • No standard-blessed way. Some people would suggest union-based solution, but this is still technically UB - though not the one which is likely to hit you. – SergeyA Sep 25 '15 at 15:07
1

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));
jrok
  • 54,456
  • 9
  • 109
  • 141
1

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.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
1

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.

Jarod42
  • 203,559
  • 14
  • 181
  • 302