So I was trying to catch SIGINT
while learning the zap
framework, and I needed to interop with C to use signal.h
. So I wrote a wrapper function that would call zap to stop if ctrl+c was called
fn zapstop(_: c_int) callconv(.C) void {
zap.stop();
}
And passed it to signal
const csignal = @cImport({
@cInclude("signal.h");
})
// ...
pub fn main() !void {
// ... other stuff ...
csignal.signal(csignal.SIGINT, zapstop);
// ... other stuff ...
}
Now I wanted to write a function that takes in a function and returns another function with the correct signature for passing to csignal.signal
. Essentially, something simple where *const fn () void
maps to *const fn (c_int) callconv(.C) void
. I tried doing the following but it didn’t work.
fn signal_wrapper(handler: *const fn () void) *const fn (c_int) callconv(.C) void {
return fn wrapped(_: c_int) callconv(.C) void {
handler();
return;
}
}
I found this article which describes how to use structs within functions to capture variables. I came up with the following minimal working example, however it doesn’t seem to compile, and the function signature is wrong, it contains an extra self
param. Any ideas how to do what I’m trying to do? (transform between function signatures)?
MWE:
const std = @import("std");
const builtin = @import("builtin");
const csignal = @cImport({
@cInclude("signal.h");
});
const SignalHandler = struct {
a: std.mem.Allocator,
handlers: handler_map,
const handler_map = std.AutoHashMap(c_int, *const fn (c_int) callconv(.C) void);
const Self = @This();
pub fn init(a: std.mem.Allocator) Self {
return Self{
.a = a,
.handlers = Self.handler_map.init(a),
};
}
pub fn deinit(self: *Self) void {
self.handlers.deinit();
}
pub fn wrap(self: Self, handler: *const fn () void) *const fn (c_int) callconv(.C) void {
const Closure = struct {
hfn: *const fn () void,
const CSelf = @This();
pub fn f(cself: *Self, _: c_int) callconv(.C) void {
cself.hfn();
return;
}
};
const C = self.a.create(Closure) catch {
std.process.exit(1);
};
C.* = Closure{ .hfn = handler };
return C.f;
}
pub fn register(self: *Self, sig: c_int, handler: *const fn (c_int) callconv(.C) void) !void {
try self.handlers.put(sig, handler);
_ = csignal.signal(sig, handler);
}
};
fn wrapme() void {
std.log.info("i love being wrapped", .{});
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const a = gpa.allocator();
defer _ = gpa.deinit();
var sigh = SignalHandler.init(a);
defer sigh.deinit();
const mewrapped = sigh.wrap(wrapme);
try sigh.register(csignal.SIGINT, mewrapped);
std.debug.print("Hello, {s}! (using Zig version: {})", .{ "world", builtin.zig_version });
}
Compilation error:
cocoa@TheTreehouse /t/tmp.6cpLkjpnMq [1]> zig build-exe -lc closures.zig (self)
closures.zig:41:20: error: no field named 'f' in struct 'closures.SignalHandler.wrap.Closure'
return C.f;
^
closures.zig:26:25: note: struct declared here
const Closure = struct {
^~~~~~
referenced by:
main: closures.zig:62:27
callMain: /home/cocoa/.local/bin/zig-linux-x86_64-0.13.0/lib/std/start.zig:524:32
remaining reference traces hidden; use '-freference-trace' to see all reference traces