If OLIVE_IMPLEMENTATION is defined before including "olive.c", then the full implementation is produced. Otherwise, just declarations, so that it behaves like a header file.
It's a valid technique. If you have a library all in one source file, the requirement to have a separate .h doubles your file count.
One small reason to have a .c suffix is might be that your editor can then choose a more specific syntax scheme. A .h file could be C++. Another one is that it can be used as a source file. You can pop it into a Makefile project, and just make sure you have -DOLIVE_IMPLEMENTATION on the compiler command line for that file. Other files using it just include "olive.c" to get the declarations. Because it has a .c suffix, make will handle it via its .c.o rule.
If you have two files: .h and .c, the use scenario may be simpler. Not a lot though. And now youu have two files which have to stay together somehow, yet remain distinct. If you combine them in one body of text, you have to indicate: oh, please snip out this part as a .h file and then the rest as a .c file. In e-mail you can have it as two separate MIME attachments. You can use an archive file --- have you looked into the formats? Not so simple.
It's not hard to understand the attraction to the one file deployment, even if you don't do it yourself.
If I wanted to distribute a single file library, but have the two file option for users, I'd make it so that a specific Awk one-liner produces the two. For instance, the file might look like this:
#ifndef FOOBAR_LIB_H_D3B94F3C
#define FOOBAR_LIB_H_D3B94F3C
// header stuff here
#endif // FOOBAR_LIB_H_D3B94F3
#if FOOBAR_LIB_IMPL
// impl here
#endif
Then to people who want two, I would say, just run this command in your shell and paste the content into it: awk 'BEGIN { print "#include \"foobar.h\"" > "foobar.c"
print "#define FOOBAR_LIB_IMPL" > "foobar.c" }
/#ifndef FOOBAR_LIB/,/#endif.*FOOBAR_LIB/ { print > "foobar.h"; next }
{ print > "foobar.c" }'
that way I wouldn't need a build step on my end to generate parallel files.I would have the extra build step if it were a large project of multiple .c files that I wanted to deploy as a single file for the users who want that. (Not only would I build the single file out of multiple files, but also have some test cases which actually use it. Things have ways of breaking when you combine files, like giving the same name two two static variables or functions in different files.)
It's possible that your linker would be smart enough to identify and remove the duplicates, but it's still inelegant, unidiomatic and needlessly inefficient.
You can #include it in any number of files as long as exactly one has #define OLIVEC_IMPLEMENTATION.
All of the function definitions are declared with OLIVECDEF, which by default is #defined as `static inline`. So if you want to only get a single copy of the implementation, you would have to choose one compilation unit that defines OLIVEC_IMPLEMENTATION, and you would have to define OLIVECDEF as something else (like the empty string) that causes the functions to be non-static.
Still kind of hacky, but not as bad as I thought.
EDIT: I just noticed that only some of the functions are marked with OLIVECDEF, so you have to do this trick if you want to reference the library in multiple compilation units, or else you would get duplicate symbols. The default behavior doesn't seem like it would ever be useful.
The macro preprocessor is like a Schrodinger's Cat of helpful and painful!
If you actually use a header-only library and it fails to link properly or be used in a project with multiple compilation units, then that would be a bug in the header-only library worth discussing and fixing. That would be a good-effort and interesting comment.
Note you get some opportunities for better compiler optimizations when the entire compilation unit is the entire project. In fact, sqlite claims the code runs 5-10% faster when built as an amalgamation (https://www.sqlite.org/amalgamation.html)
const char *s =
#include "test.txt"
;
Doing
const char *s = "
#inclued "test.txt"
";
won't work, since the preprocessor won't interpret directives inside string literals of course.In many assemblers, there is a directive called "incbin" which pastes in unstructured binary data at the point of usage. I just found a very clever C and C++ wrapper [1] for that, which gives you an INCBIN() macro. Nice!
float vertices[] = {
#include "stuff.txt"
};