how do you make a good C library?

C libraries live in a state of anarchy. I’ve been working on a little general purpose library for C because [insert reason here], here are some scattered notes:

folder organization

there are a few good ways to organize your folders:

this is sort of a classic way of doing it:

include
| lib_a.h
| lib_b.h
src
| lib_a.c
| lib_b.c

here is a more modern way of doing it

lib.h

this is called a “single header library”. the idea is that it contains everything the library needs, and implementation is provided by defining a DO_THE_IMPL macro before an include exactly once. it’s prone to slow compilation but it works for simple stuff. it’s also really nice for libraries that use a lot of macros

here’s a cute method:

src
| lib_a.c
| lib_b.c
lib_a.h
lib_b.h

I like this because it also provides an obvious solution to privacy. lib_a.h has some bit of public interface, and there could be a src/lib_inner_a.h providing internal private interfaces to whatever whatever.

namespacing

int add(int, int);

C doesn’t have namespaces. you need to prefix your functions with something to avoid conflicts with other libraries or even your user’s code.

int lib_add(int, int);

maybe you can get away with defining a macro that does it for you? that way you can even make it optional

#define LIB_NAME(name) lib_##name

int LIB_NAME(add)(int, int);

I found that this screws with IDE features.

you can also try aliasing, which can also be made optional.

int lib_add(int, int);
#define add lib_add

I don’t really like how it feels to define though…

names

coming from Rust, I started by defining names like so

struct StructName;
int function_name(void);
int variable_name;

I found that using pascal case for structs looked a bit odd in C though, for whatever reason.

a note on generic apis

how do you do generics in a language without generics? void* everywhere??

… yes.

I found a neat method for attaching type safety to void*, but it’s kinda >=C17… it goes like this.

you have your unsafe stuff:

struct vector {
	void *data;
};
void *get_ptr(struct vector *vec);

now make a macro for attaching type information using a union:

#define safe_vector(type) union { struct vector _; type *T; }

then make other macros for working with the original functions but Safe(tm)

#define safe_get_ptr(vec) (typeof(vec->T)*) get_ptr(&vec->_)

when working with it, now it looks like

int main(void) {
	safe_vector(int) vec = idk();
	int something = *safe_vector(&vec);
}

yay!

how much trust should you give the user

they’re a C programmer. C doesn’t make it easy to hide implementation details, so don’t worry about them screwing things up. just add a note something making your user pinky promise not to screw up.

consider littering your api with assert() from assert.h so that if (when) your user has an incredibly niche use case for your library and they decide to mess around with your internal structures, they at least know how badly they screwed up.

if you really don’t trust your user, you can also obfuscate everything by making everything public a void* (or similar), though this could get messy, and I would personally kill you. unrelated if anyone has the location to the creator of opengl email me