#Generic function definitions with callconv(.C)

1 messages · Page 1 of 1 (latest)

boreal solstice
#

I am writing a DLL that is intended to serve as a focal point for event handling for a server executable. The code is set up so that other DLLs loaded by this main one can add('bind') their own callbacks for a given event at runtime (so that people don't have to recompile everything if they want to add their own custom code to a server). I currently have the event handlers set up like this:

export const Status = extern struct {
    default: bool = true,
    custom: bool = true,
};

const OnServerInit_CallbackType = *const fn (Status) callconv(.C) Status;
const OnServerExit_CallbackType = *const fn (Status, bool) callconv(.C) Status;
var OnServerInit_callbacks: std.ArrayListUnmanaged(OnServerInit_CallbackType) = .{};
var OnServerExit_callbacks: std.ArrayListUnmanaged(OnServerExit_CallbackType) = .{};

pub export fn OnServerInit() void {
    var status: Status = .{};
    for (OnServerInit_callbacks.items) |cb| {
        status = cb(status);
    }
}

pub export fn bind_OnServerInit(callback: OnServerInit_CallbackType) void {
    OnServerInit_callbacks.append(event_allocator, callback) catch |err| {
        // print error message, exit
    };
}

pub export fn OnServerExit(error_state: bool) void {
    var status: Status = .{};
    for (OnServerInit_callbacks.items) |cb| {
        status = cb(status, error_state);
    }
}

pub export fn bind_OnServerExit(callback: OnServerExit_CallbackType) void {
    OnServerExit_callbacks.append(event_allocator, callback) catch |err| {
        // print error message, exit
    };
}

However, this is kind of unwieldy as I have to define the necessary parts for each event by hand. I would much prefer to wrap these in a helper function or two, but I'm not sure how the actual handler calls (and calls to callback functions) should be defined.

#

This is what I have so far:

fn CallbackType(comptime Function: anytype) type {
    return @Type(.{ .Fn = .{
        .calling_convention = .C,
        .alignment = @alignOf(Function),
        .is_generic = false,
        .is_var_args = false,
        .return_type = Status,
        .params = @typeInfo(Function).Fn.params,
    } });
}

fn Event(comptime Function: anytype, comptime export_name: []const u8) type {
    return struct {
        var callbacks: std.ArrayListUnmanaged(*const CallbackType(Function)) = .{};

        //pub fn trigger(???) callconv(.C) void {
        //    for (callbacks.items) |cb| {
        //        status = @call(.auto, cb, .{status, ???});
        //    }
        //}

        pub fn bind(callback: *const Function) callconv(.C) void {
            callbacks.append(event_allocator, callback) catch |err| {
                _ = err;
                // print error message, run cleanup code, exit
            };
        }

        comptime {
            //@export(trigger, .{
            //    .name = export_name,
            //    .linkage = .Strong,
            //});

            @export(bind, .{
                .name = "bind_" ++ export_name,
                .linkage = .Strong,
            });
        }
    };
}
#

I'm just not sure how to state what arguments a given trigger function should have to make it capable of taking X number of arguments when it's in callconv(.C) form. I don't think I can use var_args, since the executable loading my library doesn't use it with these.