Step 6 of 6
Challenge: an event bus
A plain function pointer has a limitation: the function it points at can only use its parameters and global variables. What if the callback needs to update your data, like a counter in main or a particular window in a GUI?
C's answer is the context pointer. Alongside the function pointer, you register a void *ctx, which can point at anything. Whoever calls the callback passes that ctx back unchanged. The callback casts it to the type it expects and uses it.
#include <stdio.h>
typedef void (*Visitor)(int value, void *ctx);
void for_each(const int *a, int n, Visitor v, void *ctx) {
for (int i = 0; i < n; i++) {
v(a[i], ctx);
}
}
typedef struct {
int sum;
int count;
} Stats;
static void add_to_stats(int value, void *ctx) {
Stats *s = ctx;
s->sum += value;
s->count++;
}
static void track_max(int value, void *ctx) {
int *best = ctx;
if (value > *best) *best = value;
}
int main(void) {
int data[] = {4, 9, 2};
Stats st = {0, 0};
int best = 0;
for_each(data, 3, add_to_stats, &st);
for_each(data, 3, track_max, &best);
printf("sum %d count %d max %d\n", st.sum, st.count, best);
return 0;
}
sum 15 count 3 max 9
The same for_each drives two completely different callbacks, each with its own kind of context. This "function pointer plus void * user data" pattern appears throughout real C APIs: pthread_create, qsort_r, event loops, GUI toolkits and audio libraries.
An event bus
An event bus stores several handler and context pairs. Emitting an event calls every registered handler, in order, each with its own context. Store the pairs in parallel arrays (or an array of structs), guard against overflowing the fixed capacity, and loop over them on emit.
Your turn: build a tiny event bus that stores up to 8 handler/context pairs.
int bus_on(Bus *b, Handler h, void *ctx): register; returns 0, or -1 when fullvoid bus_emit(Bus *b, int event): calls every handler in registration order with the event and its own ctx