pointers-memory · intermediate · ~15 min

Event dispatcher with user-data context

Pair function pointers with arbitrary user-data context pointers to build decoupled event-driven systems.

Challenge

Asynchronous frameworks (like GUI toolkits, libuv, and Linux kernel drivers) use callback function pointers paired with a void *user_data context pointer to maintain state without global variables.

Your Task

Implement:

typedef void (*event_cb_t)(int event_type, void *user_data);

struct event_listener {
    int event_type;
    event_cb_t cb;
    void *user_data;
};

struct event_bus {
    struct event_listener listeners[16];
    size_t count;
};

void event_bus_init(struct event_bus *bus);
int event_bus_subscribe(struct event_bus *bus, int event_type, event_cb_t cb, void *user_data);
int event_bus_publish(const struct event_bus *bus, int event_type);

Rules

  1. event_bus_init: sets bus->count = 0.
  2. event_bus_subscribe:
    • If bus == NULL, cb == NULL, or bus->count >= 16, return -1.
    • Register the listener at index bus->count and increment bus->count. Return 0.
  3. event_bus_publish:
    • If bus == NULL, return 0.
    • Iterate through all registered listeners. For each listener matching event_type, invoke l->cb(event_type, l->user_data).
    • Return the count of matching listeners that were invoked.

Input format

bus: event bus pointer; event_type: integer event id; cb: callback function; user_data: context pointer.

Output format

subscribe returns 0 or -1. publish returns count of notified listeners.

Constraints

C11 freestanding. Up to 16 subscribers.

Starter code

#include <stddef.h>

/* The harness provides:
typedef void (*event_cb_t)(int event_type, void *user_data);

struct event_listener {
    int event_type;
    event_cb_t cb;
    void *user_data;
};

struct event_bus {
    struct event_listener listeners[16];
    size_t count;
};
*/

void event_bus_init(struct event_bus *bus) {
    (void)bus;
}

int event_bus_subscribe(struct event_bus *bus, int event_type, event_cb_t cb, void *user_data) {
    (void)bus; (void)event_type; (void)cb; (void)user_data;
    return -1;
}

int event_bus_publish(const struct event_bus *bus, int event_type) {
    (void)bus; (void)event_type;
    return 0;
}

Common mistakes

Forgetting to pass user_data to the callback; not checking bus->count < 16.

Edge cases to handle

No listeners for event returns 0; multiple listeners for same event all receive invocation; full bus returns -1 on subscribe.

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.