Simple Modules in C
Coming back home today it occurred to me that it shouldn't be all that hard to create a module in C, namely something that could get one to a point where we have a clear namespace, and the functions safely hidden behind it, something like namespaces in C++ but less ... C++...
The key to doing this is, of course, everyone's favourite keyword static, namely static functions.
I had to battle with the design for a bit but here is what I came up with:
- first we define our functions that make up the module as static functions in a C file that gets compiled by itself.
- second we stick these functions into a struct through a "module initializer" function.
- third we get style points by using some suggestively named macros.
// foo_mod.c
#include "foo_mod.h"
#include "stdio.h"
static void foo() {
printf("hello from foo\n");
}
static int bar(int x, int y) {
return x * y;
}
foo_module_type foo_initialize_library() {
return (foo_module_type) {
.foo = foo,
.bar = bar,
};
}
Where only the last part (i.e. the library initialization) needs to be included in the public API. Note that this means that the symbols for foo and bar are only available through the export of foo_initialize_library which corresponds to the export command in (sane) languages with module support (notably not languages with export-all defaults like python). The header then only needs to provide the module type with the module's API.
// foo_mod.h
#ifndef FOO_MOD_H_
#define FOO_MOD_H_
typedef struct {
void (*foo)();
int (*bar)(int x, int y);
} foo_module_type;
foo_module_type foo_initialize_library();
#define LIB_DECLARE_FOO(LIB) foo_module_type LIB
#define LIB_INIT_FOO(LIB) LIB = foo_initialize_library()
#endif // FOO_MOD_H_
And if we compile the code as usual cc -I. -c foo.c and check the exported symbols with nm -g we get what we wanted:
$ nm -g foo_mod.o
00000000 W __retguard_2039
00000000 W __retguard_2907
00000000 W __retguard_749
00000000 T foo_initialize_library
00000000 F foo_mod.c
U printf
Namely foo and bar are nowhere in sight. and to use this mess, it's, as you might expect, not the most ergonomic experience, but it is less trouble than one may have set themselves up for, starting with the idea of going around C's penchant to make everything public.
// main.c
#include "foo_mod.h"
#include "stdio.h"
LIB_DECLARE_FOO(foo_lib); // declare the library as a global because why not?
int main () {
LIB_INIT_FOO(foo_lib); // initialize the library
foo_lib.foo();
int x = 3;
int y = 23;
printf("%d * %d = %d\n", x, y, foo_lib.bar(x, y));
}
Now how would it go to debug this code? I'm not sure, but as long as you refrain from writing bugs, it should be fine, but to be honest, that's pretty much how I've always seen any language that you just can't easily stick into gdb to work.
Well, that was all for now. See you around Carl.