// On the heap
struct my_struct* ms = malloc(my_struct_size());
my_construct(ms, 123, 456.0f);
// On the stack
MY_STRUCT_VAR(ms2);
my_construct(ms2, 123, 456.0f);
When I tried it here GCC complained about `ms2 = ms2_buf__` being an assignment between incompatible pointer types. That sounds like an easy way to get into trouble and undefined behavior.
I just wanted to convey the idea so I didn't compile or check the code. But now that you bring up UB, I would change the MY_STRUCT_VAR macro as follows:
Ie, changed to unsigned char and set the buffer to have the maximum (strictest) alignment. And added the cast.
It's a pretty widely-used technique and I have satisfied myself in the past that it's not UB (ie, by checking the Standard). I am no language lawyer though so don't take my word for it. The heap example, on the other hand, is pretty much identical to what an allocation inside your library would look like so feel free to disregard the stack example if you'd like.
my_lib.h:
my_lib.c: user.c: