Most error handling just consists of checking the return value of a call and propagating it up to the caller if necessary. Sometimes I also set an errno-like variable with an error message to check it in the top-level code. It's a bit wordy but obvious and sufficiently good for all my use cases.
I don't think C limits the size of the project. It's all about good organization and coming up with the right higher-level protocols/conventions. This, IMHO, is what allows or prevents the code size from scaling gracefully.
That being said, I did come across some discussions (ex: https://stackoverflow.com/questions/34724057/embed-python3-w...) where it is not possible to strip the standard library from Python 3. I think the use case of embedding strictly the interpreter without any "batteries" is not popular and thus has not been that well-maintained. I've not tested this in practice, however.
This way if you accidentally create a new language, programmers will still be able to load existing C header files without having to manually write and maintain FFI bindings.
During the development of the project, I had a thought that it would be nice to have a RAII/defer mechanism to get rid of repetitive code for freeing resources at the end of a function. But I'm not sure if that's really necessary since you can just put the 'free' calls at the end of the function and insert some labels between them in a kind of 'stack'. This perhaps is more in the spirit of the language - a bit more wordy, but having less voodoo done by the compiler.
Thank you for your response.
#define itervar(i) i##__LINE__
#define FOR_DEFER(pre, post) for (int itervar(i) = ((pre), 0);
!itervar(i)++; (post))
Foo *foos;
FOR_DEFER(foos = alloc(Foo, 1024), free(foos))
{
// This "loop" only runs once.
}
Those can be easily stacked FOR_DEFER(foos = alloc(Foo, 1024), free(foos))
FOR_DEFER(bars = alloc(Bar, 1024), free(bars))
{
///
}
Here is a memory arena macro. #define FOR_MEMORY_ARENA(name) for DEFER(Arena name = make_arena(), free_arena(&arena))
FOR_MEMORY_ARENA(arena)
{
Foo *foos = arena_alloc(&arena, Foo, 1024);
Bar *bars = arena_alloc(&arena, Bar, 1024);
// all allocations automatically freed at the end
}https://fdiv.net/2015/10/08/emulating-defer-c-clang-or-gccbl...
If you want to re-create the STL, maybe. But you can make custom data structures tailored to your task at hand instead.
For example, instead of a std::map or std::unordered_map that allocates and initializes each node separately, you could preallocate some of them in a big chunk of memory, hand them out via a bump allocator scheme, and later free them all at once. Instead of a std::sort algorithm, you could use a bucket sort if it's possible in your situation, to improve your asymptotics from O(n log n) to O(n). Etc, etc.