You're supposed to #define GIF_IMPLEMENTATION in only one of your .c files to prevent linker collisions. But yeah I don't get the point. How is this any better than the standard method of putting macros and data definitions in the .h and code in the .c? This is just going to confuse anyone who comes along later and wants to work on the code that uses this thing.
Totally fair — this pattern can be confusing at first glance.
The main motivation is ease of integration: no need to manage extra .c files, no build system tweaks, just drop in one file and go.
It’s especially useful for embedded systems, scripts, and small projects where build friction matters.
That said, I agree that for larger teams or long-term projects, the classic .h + .c split can be clearer — that’s why the implementation can easily be separated if needed.
Appreciate the feedback!
people who have to support windows do this sometimes because MS has gone for almost forty years now without ever specifying a default directory where libraries go. This is in spite of the fact that being the creator of the OS, the most-popular compiler, and the SDK should in theory give them enormous leverage to dictate where dependencies get saved.
There's this bizarre cultural difference you see between people who learned to program on windows compared to people who learned to program on unix wherein the Windows crowd don't see the value of having an easily reproducible build system because. Usually this results in some combination of needlessly-complicated scripts and checking binary builds of dependencies into version-control.
Requiring you to include a header-file while also defining a specific constant in only one place that turns the header file into a c-file is actually one of the more benign workarounds you see.
Outside FOSS UNIX clones, most OSes, including commercial OSes, tend to have a more diversified ecosystem in compilers, and which directories those compilers take their libraries from.
Not learning about how compilers, linkers and library managers actually work.
Even C++, and considering templates, if you want to use external templates optimization you need implementation files, and then there are modules (already working in clang/VC++).
So you get a header only implementation by forcing all users to do something unexpected instead of having a single C file. Seems silly and not really the point of a header only implementation.
It's not that weird, and it's explained first thing in the header and shown in the example of how to use it. You do need to read how to use the thing, and this is a simpler detail than any of the function signatures you'll have to look at.
Personally I would probably add a gif.c to my project which does nothing but include the header with the define set, at least if I'm going to need a gif decoder in more than one place. Probably many (most?) projects only need this library in one file anyway, in which case I'd just include it from that file and be done.
Great question!
It works the same way as stb-style libs: you only #define GIF_IMPLEMENTATION in one .c file (one translation unit). In all other files, you just #include "gif.h" without the define.
The header uses #ifdef GIF_IMPLEMENTATION to include implementation code only once.
So no linker errors — everything compiles cleanly as long as that rule is followed.
I’ll make this clearer in the README too, thanks!
Probably it's better to mark every functions as static in a header-only library, not just those internal ones. But I think code bloat would still be an issue if we don't use LTO or other optims across compilation units.
edit: Yeah, defining *_IMPLEMENTATION appropriately would address these issues.
Comments
It's header-only in the sense that you cut everything that normally goes in the .c file and pasted it in the .h file.
Don't you get linker errors when a project includes this header twice in different translation units? If not, please explain how.
You're supposed to #define GIF_IMPLEMENTATION in only one of your .c files to prevent linker collisions. But yeah I don't get the point. How is this any better than the standard method of putting macros and data definitions in the .h and code in the .c? This is just going to confuse anyone who comes along later and wants to work on the code that uses this thing.
Totally fair — this pattern can be confusing at first glance. The main motivation is ease of integration: no need to manage extra .c files, no build system tweaks, just drop in one file and go. It’s especially useful for embedded systems, scripts, and small projects where build friction matters. That said, I agree that for larger teams or long-term projects, the classic .h + .c split can be clearer — that’s why the implementation can easily be separated if needed. Appreciate the feedback!
Looks like you are required to #define GIF_IMPLEMENTATION before #include "gif.h" in exactly one of your translation units.
At that point, what does header-only gain you in C (ie not C++)?
I've included many single .h/.c pair libraries before in my projects and given the of simplicity that was never a pain point.
people who have to support windows do this sometimes because MS has gone for almost forty years now without ever specifying a default directory where libraries go. This is in spite of the fact that being the creator of the OS, the most-popular compiler, and the SDK should in theory give them enormous leverage to dictate where dependencies get saved.
There's this bizarre cultural difference you see between people who learned to program on windows compared to people who learned to program on unix wherein the Windows crowd don't see the value of having an easily reproducible build system because. Usually this results in some combination of needlessly-complicated scripts and checking binary builds of dependencies into version-control.
Requiring you to include a header-file while also defining a specific constant in only one place that turns the header file into a c-file is actually one of the more benign workarounds you see.
Outside FOSS UNIX clones, most OSes, including commercial OSes, tend to have a more diversified ecosystem in compilers, and which directories those compilers take their libraries from.
Naturally people love to complain about Windows.
Also nowadays there is vcpkg and conan.
Been a good while since I worked with C/C++, but when I did it was on Windows.
As I recall I just added the files to the project, possibly configured a define or five, and then compiled.
Tedious when it's tons of directories, but a breeze with just a pair of files.
That said, I get that this buys you a bit of flexibility in build management.
Not learning about how compilers, linkers and library managers actually work.
Even C++, and considering templates, if you want to use external templates optimization you need implementation files, and then there are modules (already working in clang/VC++).
So you get a header only implementation by forcing all users to do something unexpected instead of having a single C file. Seems silly and not really the point of a header only implementation.
It's not that weird, and it's explained first thing in the header and shown in the example of how to use it. You do need to read how to use the thing, and this is a simpler detail than any of the function signatures you'll have to look at.
Personally I would probably add a gif.c to my project which does nothing but include the header with the define set, at least if I'm going to need a gif decoder in more than one place. Probably many (most?) projects only need this library in one file anyway, in which case I'd just include it from that file and be done.
Great question! It works the same way as stb-style libs: you only #define GIF_IMPLEMENTATION in one .c file (one translation unit). In all other files, you just #include "gif.h" without the define. The header uses #ifdef GIF_IMPLEMENTATION to include implementation code only once. So no linker errors — everything compiles cleanly as long as that rule is followed. I’ll make this clearer in the README too, thanks!
Probably it's better to mark every functions as static in a header-only library, not just those internal ones. But I think code bloat would still be an issue if we don't use LTO or other optims across compilation units.
edit: Yeah, defining *_IMPLEMENTATION appropriately would address these issues.
"ifdef GIF_IMPLEMENTATION"