There is an anti-pattern where header files are used for all the declarations needed internally by the source file. Including (pasting verbatim with the preprocessor) that file from another module would bring in all the unnecessary declarations.
There is an anti-pattern where header files are used for all the declarations needed internally by the source file. Including (pasting verbatim with the preprocessor) that file from another module would bring in all the unnecessary declarations.
If I code in Rust or C++ I can use namespacing and public/private to give every single object in the codebase a clean interface, but in C doing that is just frustrating, not to mention potentially inefficient.
// in <opaque_foo.h>
typedef struct opaque_foo opaque_foo_t;
size_t opaque_foo_sz(void);
void opaque_foo_init(opaque_foo_t* foo)
// in your code, which you could write a helper macro for if you were so inclined
char opaque_foo_mem[opaque_foo_sz()];
opaque_foo_t * my_foo = (opaque_foo_t*) opaque_foo_mem;
opaque_foo_init(my_foo);alternatively, gcc supports VLAs in unions, but I don't think clang does, but that makes it extra annoying to do.
edit: apparently you can probably apply the may_alias attribute to the type? Or you could try using transparent_union. No idea if clang supports either...
[1] https://nullprogram.com/blog/2019/10/28/, discussed at the time at https://news.ycombinator.com/item?id=21374863
// in your code, which you could write a helper macro for if you were so inclined
opaque_foo_t *my_foo = alloca(opaque_foo_sz());
opaque_foo_init(my_foo);There is no way for a compiler to infer the alignment requirement of a struct because it does not see its definition. You would always have to align the char buffer by hand but you cannot do it for the same reason compiler cannot.
What you can do is to always greedily align the char buffer to the strictest (largest) fundamental requirement for that platform - in other words alignas(max_align_t).
And this is actually what alloca() does for you by default. From https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
> The object is aligned on the default stack alignment boundary for the target determined by the BIGGEST_ALIGNMENT_macro.
* I may be running in an environment where allocating memory is an asynchronous operation. A callback based API forces me to block, which can cause unpleasant side effects like halting an event loop.
* In my experience, some libraries with custom allocation hooks forget to define one or both of the two basics in the callback signature: a "context" or "user data" parameter, and a way to return an error.
The proper solution is to decouple memory allocation from object initialization entirely. There are two different approaches to this:
1. Expose get_foo_size() and get_foo_align() functions that return the size and alignment that the opaque foo struct needs (at runtime, of course). Then I as the user can allocate that memory, and initialize my opaque foo objects in-place:
size_t foo_size = get_foo_size();
void* buf = alloc_aligned_memory(foo_size * 1000, get_foo_align());
for(size_t i = 0; i < 1000; ++i) {
int err = foo_init(buf + i * foo_size, /* params here */);
}
2. Define foo_init(void* buf, size_t len, ...) which attempts to initialize an opaque foo object in the buffer defined by [buf, buf+len). If the buffer does not have enough space, return an error. Otherwise, return the number of bytes actually used by the object.