As part of my participation in the UMD Racing Formula Student team, I have been writing the embedded software for the low-voltage class of PCBs that are powered with STM32 MCUs. For the architecture design, we have opted to go for a framework-style of software that is completely written in C. One of the sub-projects this framework should accommodate is a testbench that is basically just an STM32F7 discovery board with a display. Unfortunately, due to time constraints, the testbench UI is designed using TouchGFX which exclusively generates C++ code from a WYSIWYG editor.
And this is where this entry’s C++ story comes from: apparently, changing the order of a struct’s members breaks the memory alignment between C (the framework), and the C++ side of the testbench code.
Every project that uses the framework is recommended to have a singular global struct which holds the entire memory usage of the PCB, and a function which initialises this state struct. For the aforementioned testbench, this struct is written in C code (a pair of .h/.c files).
This is said struct:
| |
And the global variable is defined like so:
| |
Without going into specific design decisions, this struct uses multiple modules of the framework on the same PCB (the testbench) which would normally be split into the different PCBs of the actual car.
The global struct state is used by the spoofing code of the different
hardware-facing modules of the framework, which is written in C. This same
struct is also used by the C++ code responsible for the nice TouchGFX interface
of the testbench. This is how a GUI touch-button on the testbench can be
registered as ‘spoofed’ physical button input in the framework.
It should be noted that the ’normal’ precuations of using C code in C++ were
taken; the entire C header file is wrapped with extern "C" guards, and all
integer types strictly use the standard stdint.h, and all enums use defined
sizes (modern C), and bool is not used, etc.
The real funny business happens when the spoof struct is moved within the
parent state struct. The order of the members shown in the above code snippet
do work, but if you move the spoof struct to the bottom of the state struct the
entire memory alignment of the access from the C++ side breaks. Because,
well… C++?

Notice how the values for the spoofed pedals being shown correctly in the debugger view of STM32CubeIDE…

Now notice how this C++ side breakpoint shows the memory misaligned.
You have to trust me in saying that the only change between the expected behaviour code and this nonsense is the order of the members of the struct.
Searching up online (and asking the ChatGPT), points towards a very common problem when interfacing between C and C++: sometimes the C compilation process sees a different layout than the C++ process.
If printf debugging is rookie-level debugging, then changing the order of
your variable definitions and seeing what order breaks the alignment surely
must be expert-level debugging.
After an agonizing number of recompilations with different permutations of ordering of the members, placing the ‘dashboard’ member above the spoofing struct breaks the layout, and placing the dashboard member below the spoofing struct unbreaks the layout (and for some reason, the spoofing struct should not be the last member in the struct).
For completeness sake, here is the definition of the UMDR_dashboard struct:
| |
This is a WIP module. The xTaskHandle is just a pointer. and the enum has a
specified size of uint16_t, so it should work…
As debugging weird compiler behaviour is not an activity I want to spend leisure time on, I would be designing around this limitation of whatever part of the compiler this is, by just simply splitting the spoofing & state and maybe integrating the complete system as a module in the framework, this way it is pure C with C++ style getters/setters (yuck).
How the C++ compiler looks at me after I have the wrong order of struct-members (I committed the sin of mixing C and C++):
