A header-only C vector database library
github.com
github.com
I also don't think it has any indexes either? So search performance is a function of the number of entries.
This is also why everyone implements their own (buggy) linked-list implementations, etc.
And header-only is more efficient to include and build with than header+source.
Now that I'm thinking about it, CMake also isn't particularly good at this the way most people use it.
But, more commonly I've seen that it's just easier to not need to add C files at all. Add a single include path and you can avoid the annoyances of vendoring dependencies, tracking upstream updates, handling separate linkage, object files, output paths, ABIs, and all the rest. Something like Cargo does all of this for you, which is why people prefer it to calling rustc directly.
I tried to use cargo in the past and found it very bad compared to apt / apt-get (even when ignoring that it is a supply-chain disaster), essentially the same mess as npm or pip. Some python packages certainly wasted far more time of my life than all dependencies for C projects I ever had deal with combined.
Apt is fine for local development, but it's a bit of a disaster for portability and reproducibility. Not uncommon to see a project where the dependencies either have unspecified versions whose latest versions in universe are incompatible, or where the package has been renamed and so you have to search around to find the new name. Plus package name conventions are terrible. libbenchmark-dev, libgoogle-perftools-dev, and libgtest-dev are all library packages from the same company. The second one is renamed to gperftools-lib with RPM repos, to further compound the inconsistency.
I find myself dealing with package and versioning rename issues regularly in the CI pipelines I have using apt.
My experience with dependencies in Debian-derived distributions is actually very good, far less problematic than any other packaging system I ever used. But yes, one needs to maintain dependencies separately for RPM and others distributions. But obviously the problem is not lack of a package manager and adding another one would not solve anyhing: https://xkcd.com/927/ The solution would be standardizing package names and versions across distributions.
Dispute.
This is C code. You can't just drop it in and build it; you have to write code to use it. You have to figure out the API to correctly use it. If memory is passed around by pointers, you have to understand the responsibilities: who allocates, who frees, who may touch what when.
In the first place, you have to decide whether to commit to that library that far; it might not be until you've done some exploratory programming with it that you want to scrap it and find another one.
The cost of adding two files versus one is practically nothing in consideration of the larger picture.
The separate header model is baked into the C mindset; the tooling readily supports it.
Many "header only" libraries are secretly two files in one. When you define a certain preprocessor symbol before including the file, you get the implementation bits. When you don't define that symbol, the header is a pure header.
That means you have to pick some existing .c file which will define the preprocessor symbol and include the heaader. That source file becomes a "surrogate" source file, taking the place of the source file it ought to have.
In reality header only libraries allow for deep inlining, the compiler may optimize very specifically to your code and usage.
The situation is a bit more exaggerated with C++ because of templates, but there is some remaining gains to he had in C alone.
https://tartanllama.xyz/posts/inline-hints/
This library seems to have the annotation on every function, though, so it's possible the author is just following a convention of always using it for functions defined in header files (it'd be required if the functions weren't declared `static`).
In the former case the compiler is allowed to always inline the function.
In the latter case, even when the compiler chooses to inline the function, it also emits code for an independent instance of the function, because the function is public and it may be called from another file.
So "static inline" in the worst case does nothing, but it suggests to the compiler that the function should be inlined everywhere, which it will probably do, unless it decides that the function is too long (or it uses some features forbidden in inlined functions, e.g. variadic arguments, setjmp, alloca, etc.), so the benefits of inlining it may be less than the disadvantages.
When the compiler refuses to follow the suggestion of inlining the function, it can be made to tell the reason, e.g. with "-Winline".
So the compiler does not ignore the suggestion, even if it may choose to not follow it.
Not in standard C. "inline" function provides implementation for usage iff compiler decides to inline the call. If it does decide not to inline, it will emit call to external symbol that needs to be defined in different TU (otherwise you will get errors at link time).
Quote from the gcc manual:
"GCC implements three different semantics of declaring a function inline. One is available with -std=gnu89 or -fgnu89-inline or when gnu_inline attribute is present on all inline declarations, another when -std=c99, -std=gnu99 or an option for a later C version is used (without -fgnu89-inline), and the third is used when compiling C++."
Nevertheless, "static inline" means the same thing in all 3 standards, unlike "inline" alone.
This can be a reason to always prefer "static inline", because then it does not matter whether the program is compiled as C or as C++.
Get over it. Some people like header only.
To comment on this, I have a couple of header-only projects I have written. It makes sense in some scenarios. Sometimes I want no external dependencies and a single header file interface.
In strict terms when you place implementation in a .c file you probably want that code to be shared when different things call it, and the compiler will "link" to that same implementation.
When you have a header only library the compiler is free to optimize in more ways specific to your actual use case.
I can still push the file on git and it works everywhere else.
But it does successfully replaces the need of using one, with less problems for certain situations.
If you do not need the features of a package manager, then git submodules is great indeed.
Package managers already use git where appropriate under the hood. Arch's PKGBUILD sources pull from git repos, FreeBSD's ports can fetch from git, Gentoo ebuilds support git-r3. The fetching is not the hard part. What a package manager gives you on top of that is dependency resolution, version constraints, conflict detection, reproducible builds, and clean removal. "git push" gives you none of that.
And since I did not really address kreco's point properly: if the concern is cross-platform support across many OSes, tools like vcpkg and Conan exist specifically for this: cross-platform C/C++ dependency management with one workflow regardless of target. pkgsrc runs on 20+ platforms with the same build recipes. A header file in a git repo gives you none of that, it just gives you the illusion of portability until you hit a platform where it does not compile or links against the wrong thing.
If your workflow does not need any of those features, then yeah, sure, but then you are not solving C's "package management problem", you are just ignoring it.
In any case, the original comment called C's package management "unfriendly", and the counter-argument has somehow arrived at "just copy files into your repo and push". Comparing that to what a package manager does is absurd.
It's a tradeoff people make between ease of integration - just download the .h file into your project folder and #include it in your source file instead of worrying about source build system vs target build system, cross compiling headaches etc...
And compilation times: any time you change any of your source files, your compiler also has to recompile your dependencies. (Assuming you haven't used precompiled headers).
int add(int a, int b){
// Long logic and then this
return a+b;
}
Let's say this is your main.c. #include "add.h"
int main(void) {
return add(5,6);
}
The preprocessor just copies the contents of add.h into your main.c whenever you're trying to compile main.c. (let's ignore the concept of precompiled headers for now).What you can instead do is just put the add function declaration in add.h that just tells the compiler that add function takes two integers and returns an integer.
int add(int a, int b);
You can then put the add function definition in add.c , compile that to an add.o and link it to your main.o at link time to get your final binary - without having to recompile add.o every time you change your main.c.Precompiled headers: https://maskray.me/blog/2023-07-16-precompiled-headers
The whole thing is essentially a workaround for lack of sufficiently good/easy ways to package code in the ways people want to use it.