Limited "generics" in C without macros or UB June 5, 2017 on Drew DeVault's blog

I should start this post off by clarifying that what I have to show you today is not, in fact, generics. However, it’s useful in some situations to solve the same problems that generics might. This is a pattern I’ve started using to reduce the number of void* pointers floating around in my code: multiple definitions of a struct.

Errata: we rolled this approach back in wlroots because it causes problems with LTO. I no longer recommend it.

Let’s take a look at a specific example. In wlroots, wlr_output is a generic type that can be implemented by any number of backends, like DRM (direct rendering manager), wayland windows, X11 windows, RDP outputs, etc. The wlr/types.h header includes this structure:

struct wlr_output_impl;
struct wlr_output_state;

struct wlr_output {
    const struct wlr_output_impl *impl;
    struct wlr_output_state *state;
    // [...]
};

void wlr_output_enable(struct wlr_output *output, bool enable);
bool wlr_output_set_mode(struct wlr_output *output,
    struct wlr_output_mode *mode);
void wlr_output_destroy(struct wlr_output *output);

wlr_output_impl is defined elsewhere:

struct wlr_output_impl {
    void (*enable)(struct wlr_output_state *state, bool enable);
    bool (*set_mode)(struct wlr_output_state *state,
        struct wlr_output_mode *mode);
    void (*destroy)(struct wlr_output_state *state);
};

struct wlr_output *wlr_output_create(struct wlr_output_impl *impl,
        struct wlr_output_state *state);
void wlr_output_free(struct wlr_output *output);

Nowhere, however, is wlr_output_state defined. It’s left an incomplete type throughout all of the common wlr_output code. The “generic” part is that each output implementation, in its own private headers, defines the wlr_output_state struct for itself, like the DRM backend:

struct wlr_output_state {
    uint32_t connector;
    char name[16];
    uint32_t crtc;
    drmModeCrtc *old_crtc;
    struct wlr_drm_renderer *renderer;
    struct gbm_surface *gbm;
    EGLSurface *egl;
    bool pageflip_pending;
    enum wlr_drm_output_state state;
    // [...]
};

This allows implementations of the enable, set_mode, and destroy functions to avoid casting a void* to the appropriate type:

static struct wlr_output_impl output_impl = {
    .enable = wlr_drm_output_enable,
    // [...]
};

static void wlr_drm_output_enable(struct wlr_output_state *output,
        bool enable) {
    struct wlr_backend_state *state =
        wl_container_of(output->renderer, state, renderer);
    if (output->state != DRM_OUTPUT_CONNECTED) {
        return;
    }
    if (enable) {
        drmModeConnectorSetProperty(state->fd,
            output->connector,
            output->props.dpms,
            DRM_MODE_DPMS_ON);
        // [...]
    } else {
        drmModeConnectorSetProperty(state->fd,
            output->connector,
            output->props.dpms,
            DRM_MODE_DPMS_STANDBY);
    }
}

// [...]
struct wlr_output output = wlr_output_create(&output_impl, output);

The limitations of this approach are apparent: you cannot work with multiple definitions of wlr_output_state in the same file. However, you get improved type safety, have to write less code, and improve readability.

Articles from blogs I read Generated by openring

On how to enhance Alpine Linux user experience

Very recently I had the need for a watch-party solution. Syncplay seems a formidable tool for this. It allows me and my partner to watch videos from our respective Mpv players. Fortunately it was already packaged for our Linux distribution, formerly Alpin…

via Willow's feed October 19, 2025

Using a Blackmagic Design camera as wildlife camera

The sane solution to capturing wildlife going through your garden is to use a dedicated wildlife camera. In my experience these things are pretty much always trash. The devices I've seen are all basically cheap dash-cam boards that happen to have moti…

via BrixIT Blog October 17, 2025

Geoblocking Multiple Localities With Nginx

A few months back I wound up concluding, based on conversations with Ofcom, that aphyr.com might be illegal in the UK due to the UK Online Safety Act. I wrote a short tutorial on geoblocking a single country using Nginx on Debian. Now Mississippi’s 2024 HB…

via Aphyr: Posts October 11, 2025