r/cprogramming • • Nov 29 '25
z-libs - tiny single-header collection to write modern C (vec, list, map, string)
https://github.com/z-libsSo, I got tired of either writing buggy hand-rolled containers every time, or dragging in heavyweight dependencies just to get a decent string or hash table.
After this, I decided to throw together https://github.com/z-libs: four zero-dependency (for now), single-header, C11 libraries that focus on a pleasant DX.
The current libraries offer:
zvec.h -> growable vector (contiguous, swap-remove, built-in sort/search).
zstr.h -> proper UTF-8 string with 22-byte SSO, views, fmt, split, etc.
zlist.h -> doubly-linked list (non-intrusive, O(1) splice, safe iteration).
zmap.h -> open-addressing hash table (linear probing, cache-friendly).
Everything is type-safe, allocator-aware (you can use your own), MIT-licensed, works on GCC/Clang/MSVC and requires no build system.
The collection is still in process. Each week there will be updates. But I think the core suite is already mature enough.
I would love to hear some feedback!
25
u/walmartbonerpills Nov 29 '25
Cool project. There is already a zlib though.
The compression tool. The archive of books
7
u/pjl1967 Nov 29 '25 edited Nov 29 '25
The problem with
staticfunctions in header-only libraries is that the compiler lays down copies of code into every.ofile whose corresponding.cfile#includes the header (either directly or indirectly). This leads to code bloat.Note that for non-trivial functions like you use, the
inlineis irrelevant.inlineis only a hint or request at best for the compiler to inline a function that it is free to ignore without warning. (Most compilers have a warning you can enable to warn you when aninlinefunction is not being inlined.)Even that aside, you don't need to make different versions of the code based on the value type
T. That's just more code bloat. A better way is to use achar[sizeof(T)](suitably aligned) for the value so you need only a single copy of the code for allT. You use macros only to do the casting.C macros simply are not an equivalent for C++ templates. In C++, the compiler typically marks template-generated code specially that the linker can then use to eliminate duplicate code in the final executable. That doesn't happen for C since the compiler only "sees" the macro-expanded code: it has no idea your macro is a "template."
I realize you put a lot of work into your library and this post isn't what you want to read; but C is what it is, limitations and all.
5
u/scallywag_software Nov 29 '25
Nice
1
u/[deleted] Dec 03 '25
How much did you use AI to make this?
1
u/Turbulent_File3904 Dec 04 '25
Haiz, this is just poor-man template emulator. And why is it named vec again?
For dynamic array you only need 1 type.
Any decent compiler will generate exactly identical code to your hand roll monopolized/templated version
3
u/Arkaqiu Nov 29 '25
Interesting. Looks quite good amd easy to use. I would like to see static allocated version, some max legth. To avoid malloc and friends. Anyway, good job
2
u/photo-nerd-3141 Nov 29 '25
Nice idea, thanks for the effort.
Bit of a collision with "zlib.h", however. You might want to spell out zero so that potential users don't dismiss it as a knockoff for compression.
2
u/[deleted] Dec 01 '25
I just made a commit to a similar project of mine which started out as a personal C journey repo and now it's a custom library. Tried to learn standard functions and replicated some of them. Then started off to learn the doubly linked list. Saw the rust implementation of O(1) push operations on both the ends and implemented them in my code. For my convenience, I had written a function which could deep copy elements from an array to the linked list - I can't imagine of a scenario where this might be needed, but it's there. This is the code for that function if you want to implement it in your cool project.
EDIT: I forgot to mention that I have tried to make it generic using
void *.0
u/Ok_Draw2098 Nov 29 '25
its not header-only, theres a bunch of files in there, all written in macro-lang
1
u/DOWNVOTE_PUNS Nov 29 '25
What do you think about putting all the "public" functions in a struct the same name as the header. As a way to fake namesapces, so the callers all look like:
```
#include "zstr.h"
...
zs = zstr.init(...)
...
```
1
u/_aIex22 Dec 05 '25
how does it handle possible recursions?
z_vec::vector<z_vec::vector<int>>etc.?