Handling errors in C-library callback functions

The switch is probably the most pragmatic solution, I’m not sure why you think it adds a lot of noise. If you mean that you don’t want to have to duplicate the mapping from Zig errors to integer status codes at every point you want to use try you could try delegating the mapping to an outer wrapper function:

fn myCallbackC(args: ?[*:0]const u8, context: ?*anyopaque) callconv(.c) c_int {
    myCallbackZig(args, @ptrCast(@alignCast(context.?))) catch |err| {
        return switch (err) {
            error.Foo => c.api_result_foo,
            error.Bar => c.api_result_bar,
            // ...
            else => c.api_result_unknown,
        };
    };
    return c.api_result_success;
}

fn myCallbackZig(args: ?[*:0]const u8, context: *MyCallbackContext) !void {
    const foo = try getFoo(...);
    const bar = try getBar(...);
    // ....
}

If you have multiple distinct callbacks but they all need to be handled the same way you could probably figure out a way to make a generic wrapper so that you only need to write the error mapping once.

You might also want to consider whether or not you want to log the error and return trace in the outer wrapper function. If the callbacks are synchronous it is possible to preserve errors and return traces across extern calls with a bit of advanced tinkering.

2 Likes