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.

Have a comment on one of my posts? Start a discussion in my public inbox by sending an email to ~sircmpwn/public-inbox@lists.sr.ht [mailing list etiquette]

Articles from blogs I read Generated by openring

Status update, August 2020

Hi! Regardless of the intense heat I’ve been exposed to this last month, I’ve still been able to get some stuff done (although having to move out to another room which isn’t right under the roof). I’ve worked a lot on IRC-related projects. I’ve added a znc-i…

via emersion 2020-08-19 00:00:00 +0200 +0200

What's cooking on Sourcehut? August 2020

Another month passes and we find ourselves writing (or reading) this status update on a quiet, rainy Sunday morning. Today our userbase numbers 16,683 members strong, up 580 from last month. Please extend a kind welcome to our new colleagues! Thanks for read…

via Blogs on Sourcehut 2020-08-16 00:00:00 +0000 +0000

Go 1.15 is released

Today the Go team is very happy to announce the release of Go 1.15. You can get it from the download page. Some of the highlights include: Substantial improvements to the Go linker Improved allocation for small objects at high core coun…

via The Go Programming Language Blog 2020-08-11 11:00:00 +0000 +0000

North Pacific Logbook

The passage from Japan (Shimoda) to Canada (Victoria) took 51 days, and it was the hardest thing we've ever done. We decided to keep a logbook, to better remember it and so it can help others who wish to make this trip.Continue Reading

via Hundred Rabbits 2020-07-31 00:00:00 +0000 GMT