0

I receive some bytes and then I want to cast to typedef struct with correspondent values. My typedef struct is:

typedef struct SomethingHeader {
   uint8_t PV;
   uint8_t messageID;
   uint32_t stID;
} ; 

I receive the array with values:

somePDU[0] = {char} 2 [0x2]
somePDU[1] = {char} 6 [0x6]
somePDU[2] = {char} 41 [0x29]
somePDU[3] = {char} -90 [0xa6]
somePDU[4] = {char} 28 [0x1c]
somePDU[5] = {char} -93 [0xa3]
somePDU[6] = {char} 55 [0x37]
somePDU[7] = {char} -50 [0xce]
somePDU[8] = {char} 0 [0x]
....

When I use reinterpret_cast<SomethingHeader*>(somePDU), on the watch debug mode I see:

PV = 2 [0x2]
messageID = 6 [0x6]
stID = -835214564 [0xce37a31c]

The reinterpret_cast jumps two bytes: somePDU[2] and somePDU[3], but I needed, because my expected value is 698797623 (0x29a6ce37)

It seems to me that the reinterpret_cast only works well every 4 bytes (or with structures that occupy 4 bytes in a row).

How can I force the reinterpret_cast not to skip those two bytes?

Null
  • 1,950
  • 9
  • 30
  • 33
  • 2
    See [Why isn't sizeof for a struct equal to the sum of sizeof of each member?](https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member) Either you need to explicitly "unpack" or "deserialize" the array into the structure, or you need to use compiler-specific extensions to "pack" the structure. – Some programmer dude Mar 01 '21 at 13:46
  • 2
    As a side-note, in C++, struct names are type names, so there is no need for the `typedef` as in C. – Ron Mar 01 '21 at 13:48
  • 5
    You need to read up on padding and endianness. – molbdnilo Mar 01 '21 at 13:49
  • 5
    Also might want to read up on the strict aliasing rule. – rustyx Mar 01 '21 at 13:55
  • Thank you for all. Yes, problem with padding and endianness. I will answer my question with my solution for others ! – TheKkilo Mar 01 '21 at 14:37
  • 1
    You shouldn't use reinterpret_cast in such cases anyway. Jason Turner just made a video about it a week ago I believe. Rather use std::memcpy: "When it is needed to interpret the bytes of an object as a value of a different type, std::memcpy or std::bit_cast (since C++20)can be used" - cppreference – Andreas Brunnet Mar 01 '21 at 14:41
  • @AndreasBrunnet `std::bit_cast` cannot be used for this. – eerorika Mar 01 '21 at 14:47
  • @eerorika I didnt't mention bit_cast. you should use memcpy in that case. Well and if you take a look at bit_cast under the hood it does nothing else than a memcpy. – Andreas Brunnet Mar 01 '21 at 15:59
  • @AndreasBrunnet `I didnt't mention bit_cast` For some reason, I can still see it in the comment `... or std::bit_cast (since C++20)can be used`. – eerorika Mar 01 '21 at 16:08
  • @eerorika Oh you mean the citation. What did the citation say before that? `std::memcpy`. Did I (not the citation - I don't like butchering citations if not necessary) mention it? No. `Rather use std::memcpy`. – Andreas Brunnet Mar 01 '21 at 16:11

2 Answers2

2
  1. You cannot use reinterpret_cast for this in C++. You will violate type aliasing rules and the behaviour of the program will be undefined.

  2. You cannot define the struct in such way that it won't have padding in standard C++. A structure like this is not a portable way to represent byte patterns in C++.

A working example:

std::size_t offs = 0;
SomethingHeader sh;
std::memcpy(&sh.PV,        somePDU + offs, sizeof sh.PV);
offs += sizeof sh.PV;
std::memcpy(&sh.messageID, somePDU + offs, sizeof sh.messageID);
offs += sizeof sh.messageID;
std::memcpy(&sh.stID,      somePDU + offs, sizeof sh.stID);
offs += sizeof sh.stID;

Note that this still assumes that the order of bytes of the integer are in native endianness which is not a portable assumption. You need to know the endianness of the source data, and convert it to native byte order. This can be done portably by shift and bitwise or.

eerorika
  • 232,697
  • 12
  • 197
  • 326
-1

My problem are padding and endianness that are in comments on my question.

My solution for padding is:

#pragma pack(push,1)
struct SomethingHeader {
   uint8_t PV;
   uint8_t messageID;
   uint32_t stID;
};
#pragma pack(pop)

For the endianness just use ntohl().

Thank you