#Function pointers and C interop

1 messages · Page 1 of 1 (latest)

gilded bison
#

I'm trying to use a small C library (kthread, link below) which has a parallel for loop construct, and takes a function pointer as argument. Is there a way to make Zig function pointers work with the C counter part easily?

const std = @import("std");

// https://github.com/lh3/minimap2/blob/master/kthread.[hc]
const c = @cImport({
    @cInclude("kthread.h");
});

fn worker_fun(data: *anyopaque, i: c_long, tid: c_int) void {
    _ = tid;
    _ = data;
    std.debug.print("ok computer {d}\n", .{i});
}

pub fn main() !void {
    // void kt_for(int n_threads, void (*func)(void*,long,int), void *data, long n);
    c.kt_for(8, &worker_fun, .{}, 100);
}

With this code I'm getting the compilation error:

par.zig:16:17: error: expected type '?*const fn (?*anyopaque, c_long, c_int) callconv(.C) void', found '*const fn (*anyopaque, c_long, c_int) void'
    c.kt_for(8, &worker_fun, .{}, 100);
                ^~~~~~~~~~~

Thanks.

prisma sinew
#

You need to specify the calling convention callconv(.C)

#
fn worker_fun(data: *anyopaque, i: c_long, tid: c_int) callconv(.C) void
gilded bison
#

Ah gotcha - that makes sense, even though it was a new zig feature for me

#

I also noticed now, that the *anyopaque should be optional, that is ?*anyopaque.

#

And now it compiles 🙂 - thanks for the help

gilded bison
#

Trying to send data through the C api back into callback gives me some new tricky things:

#

I added a struct to pass through, but I'm not sure how to cast the ?*anyopaque back to the struct pointer.

const std = @import("std");

// https://github.com/lh3/minimap2/blob/master/kthread.[hc]
const c = @cImport({
    @cInclude("kthread.h");
});

const DummyData = struct {
    i: i64,
    j: i64,
};

fn worker_fun(data: ?*anyopaque, i: c_long, tid: c_int) callconv(.C) void {
    var d: ?*DummyData = @ptrCast(data);
    _ = tid;
    std.debug.print("ok computer {d}\n", .{d.i + i});
}

pub fn main() !void {
    // void kt_for(int n_threads, void (*func)(void*,long,int), void *data, long n);
    var data = DummyData{ .i = 12, .j = 24 };
    c.kt_for(8, &worker_fun, &data, 100);
}
#
    var d: ?*DummyData = @ptrCast(data);
                         ^~~~~~~~~~~~~~
par.zig:14:35: note: '?*anyopaque' has alignment '1'
    var d: ?*DummyData = @ptrCast(data);
                                  ^~~~
par.zig:14:26: note: '?*par.DummyData' has alignment '8'
par.zig:14:26: note: use @alignCast to assert pointer alignment
prisma sinew
#

This is a common pattern. You need @alignCast(@ptrCast(...))

gilded bison
#

Ah so just forcing it back to the original alignment then I guess?

prisma sinew
#

Yes, @alignCast asserts that the pointer has the right alignment (checked UB).