I'm trying to figure out best practices in C(11). Because there are no namespaces I can think of two approaches to avoid name collisions:
a.) Function prefixes
void kernel_init(void) { ... }
int kernel_start(void* foo) { ... }
b.) "Function structs"
struct kernel {
void (*init)(void);
int (*start)(void* foo);
} kernel;
I'm not asking which approach is prettier because that's rather subjective. What I'm asking is are there any noticeable downsides to either approach apart from code style? This includes little things that are irrelevant at first but become larger problems once the code base grows.
Interesting, I had never thought of that solution before.
The first one is of course standard, and I'd bet that's what you would find in the vast majority of C projects[*].
The second in theory takes memory, since you're actually declaring a data object full of function pointers. You would of course also need to initialize the kernel
variable, i.e. have something like:
...
} kernel = {
.init = kernel_init,
.start = kernel_start,
};
But oooh there you go with prefixed functions again. To remove the need for those, the functions have to be static
, which I guess is possible if you add extern
to the struct
declaration in the kernel.h
.
So a more complete example could be:
// kernel.h (public header)
typedef struct kernel_api {
void (*init)(void);
int (*start)(void* foo);
} kernel_api;
extern const kernel_api kernel;
// in kernel.c
static void init(void)
{
...
}
static int start(void *foo)
{
..
}
const kernel_api kernel = {
.init = init,
.start = start,
};
This might work, but I haven't tried it.
Finally, having explicit data means it takes a sufficiently smart compiler to optimize those out and make more direct calls, but I haven't tried it and it's a bit risky to depend on such. Interesting.
[*] I think I just statistically claimed that I have seen (or thought of) the vast majority of the world's C projects, but that's of course not true. :)
The function version is what's most commonly used.
The down-side of the struct version is that it's not stand-alone. The function pointers should be set from a "constructor", not from the caller, since that would violate private encapsulation design practice. And what would you name your constructor? You're going to need a prefixed function no matter.
Structs like this are often just used when you wish to achieve polymorphism or when you have callback functions. Some coding styles also use structs to emulate C++ class members, but whether or not that is actually good practice is debatable.
Best OO practice rather calls for a 3rd version, using opaque pointers, meaning you need both functions and structs:
typedef struct kernel_t kernel_t;
kernel_t* kernel_init (void) { ... }
int kernel_start (kernel_t* this, ...) { ... }
where the struct definition is only visible to "kernel.c", not to the caller.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With