I am trying to hide some implementation behind an opaque data type. Normally, pimpl would be the preferred pattern, but it requires heap allocation which is not desirable in this context (embedded, interrupt handler, high IQRL, etc.).
The way I saw this implemented before is to make the opaque type the right size so it can be kept on stack by the caller, then cast it to implementation:
// *.h
struct opaque_handle {
alignas(max_align_t) char _storage[128];
};
void set_x(opaque_handle* handle, int x);
int get_x(const opaque_handle* handle);
// *.c
struct impl {
int x;
};
static_assert(sizeof(struct impl) <= sizeof(struct opaque_handle)), "Insuficcient handle size");
void set_x(opaque_handle* handle, int x) {
((const struct impl*) handle)->x = x;
}
// user
int get_x(const opaque_handle* handle) {
return ((const struct impl*) handle)->x;
}
// user
struct opaque_handle handle = { 0 };
set_x(&handle, 5);
assert(get_x(&handle) == 5);
But this involves type punning, which is UB. Is there a way to implement get_x in a compliant way?