They're not an ideal solution by any stretch, but it's not the nightmare scenario you envision wrt generic data structures in C.
typedef unsigned char uchar;
DECLARE_AND_IMPLEMENT_ARRAY_API(uchar)It does make debugging a chore. I ended up with a Makefile rule to run the test suite through the preprocessor (and did some hackery to exclude #include of system headers), format it with clang-format, and build that. Not exactly pretty or easy, but it got the job done.
1. https://github.com/alpha123/yu/blob/master/src/yu_splaytree....
2. https://github.com/alpha123/yu/blob/master/test/test_splaytr...
3. Where this sort of thing is much easier and I am happier and more productive.
Pointing into subsections works fine. You just have to create a type for it. This solution doesn't have the same problems as strings because you don't rely on a terminating entry, and it's what languages like Rust or Java do as well.
You can allocate dynamic arrays on the stack in C just fine with alloca(). The only performance cost is when checking bounds, but since it's a dynamic array, it's the same cost you'd pay in Rust.
alloca-style variable arrays is a whole other can of worms of danger and complexity.
https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html
Legal in C99, available in C++ on gcc/clang.
> A beginner will often try something along the lines of size = sizeof( myarray ) (which is incorrect).
Additionally, all the functions should be inlined anyway because the function call overhead will likely be as much or more than the actual code, and, more importantly, inlining enables other optimisations (removing the branch, vectorising the memory access, etc.). Once inlined, the code will be the same as the manual/macro-based approach of writing `if` statements around each array[index] access.
- be implemented with macros and token pasting, and result in a ton of mental overhead because you'll have a pile of types like array_foo for an array of `foo`s, and array_bar for an array of `bar`s, along with a pile of corresponding `foo * array_foo_get(array_foo, size_t)` and `bar * array_bar_get(array_bar, size_t)` functions.
- or, have a runtime cost and lose type safety by storing void* and casting when accessing.
The first case is even worse than it sounds: e.g. I don't know how you handle arrays of types with spaces in them (like `unsigned char`, or `struct bar`) with a macro. And, we haven't even thought about const correctness yet, which would probably require having const_array_foo, const_array_bar (etc.) types defined too.
(And, of course, these only solve one facet of the problems with C's pointers: there's no way to defend against use-after-free or dangling pointers.)