I have some code on a Cortex-M4 microcontroller and'd like to communicate with a PC using a binary protocol. Currently, I'm using packed structs using the GCC-specific packed
attribute.
Here is a rough outline:
struct Sensor1Telemetry { int16_t temperature; uint32_t timestamp; uint16_t voltageMv; // etc... } __attribute__((__packed__)); struct TelemetryPacket { Sensor1Telemetry tele1; Sensor2Telemetry tele2; // etc... } __attribute__((__packed__));
My question is:
TelemetryPacket
struct on the MCU and the client app, will the above code be portable accross multiple platforms? (I'm interested in x86 and x86_64, and need it to run on Windows, Linux and OS X.)EDIT:
The term "closest packed structures" refers to the most tightly packed or space-efficient composition of crystal structures (lattices). Imagine an atom in a crystal lattice as a sphere. While cubes may easily be stacked to fill up all empty space, unfilled space will always exist in the packing of spheres.
padding makes things bigger. packing makes things smaller.
The #pragma pack directive modifies the current alignment rule for only the members of structures whose declarations follow the directive. It does not affect the alignment of the structure directly, but by affecting the alignment of the members of the structure, it may affect the alignment of the overall structure.
Considering the mentioned platforms, yes, packed structs are completely fine to use. x86 and x86_64 always supported unaligned access, and contrary to the common belief, unaligned access on these platforms has (almost) the same speed as aligned access for a long time (there's no such thing that unaligned access is much slower). The only drawback is that the access may not be atomic, but I don't think it matters in this case. And there is an agreement between compilers, packed structs will use the same layout.
GCC/clang supports packed structs with the syntax you mentioned. MSVC has #pragma pack
, which can be used like this:
#pragma pack(push, 1) struct Sensor1Telemetry { int16_t temperature; uint32_t timestamp; uint16_t voltageMv; // etc... }; #pragma pack(pop)
Two issues can arise:
movaps
or ldrd
), then you may get a crash using that pointer (gcc doesn't warn you about this, but clang does).Here's the doc from GCC:
The packed attribute specifies that a variable or structure field should have the smallest possible alignment—one byte for a variable
So GCC guarantees that no padding will be used.
MSVC:
To pack a class is to place its members directly after each other in memory
So MSVC guarantees that no padding will be used.
The only "dangerous" area I've found, is the usage of bitfields. Then the layout may differ between GCC and MSVC. But, there's an option in GCC, which makes them compatible: -mms-bitfields
Tip: even, if this solution works now, and it is highly unlikely that it will stop working, I recommend you keep dependency of your code on this solution low.
Note: I've considered only GCC, clang and MSVC in this answer. There are compilers maybe, for which these things are not true.
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