Zig: Trouble passing function pointer (w/ non-void return) to C API

Created on 21 Oct 2018  路  3Comments  路  Source: ziglang/zig

Background

I'm having trouble passing a function pointer into a C API.

The Tetris example demonstrates passing Zig function to a C API for use as a callback.

Failing Case

However, when I attempt to perform the same (calling into GNU Guile Scheme), I receive:

error: expected type '?*c_void', found '*const extern fn(?[*]struct_scm_unused_struct) ?[*]struct_scm_unused_struct'

fn real_fact(x: c_int) c_int {
    if(x == 1) { 
        return 1;
    } else  {
        return x * real_fact(x-1);
    }
}

extern fn c_fact(arg: SCM) SCM {
  const c_arg: c_int = scm_to_int(arg);
  return scm_from_int( real_fact(c_arg));
}

pub fn main() void {
    scm_c_define_gsubr(c"c-fact", 1, 0, 0, c_fact);
}

Attempted Solutions

I've attempted to use &c_fact instead of c_fact. No luck.

I've attempted to use @ptrCast(*c_void, c_fact), but this results in "cast discards const qualifier." Not sure how to discard the const qualifier of a fn.

One last thought: I've only seen void callback fns succeed so far. Could it be that my case is failing because c_fact has a return type?

Thanks again for the great work. Enjoying the language quite a bit so far.

question

Most helpful comment

If you really need to discard a const from a pointer then the following way will work:

pub fn main() void {
    var c_fact_var = @intToPtr(?*c_void, @ptrToInt(c_fact));
    _ = scm_c_define_gsubr(c"c-fact", 1, 0, 0, c_fact_var);
}

I don't think there is much better here since the function requiring a *c_void type is fairly limiting. If there are stronger guarantees on the api (actually doesn't modify values) you could declare the function yourself with a *const c_void or manually use zig translate-c and modify as needed, then the ptrCast should work as expected.

The return value shouldn't matter here.

All 3 comments

If you really need to discard a const from a pointer then the following way will work:

pub fn main() void {
    var c_fact_var = @intToPtr(?*c_void, @ptrToInt(c_fact));
    _ = scm_c_define_gsubr(c"c-fact", 1, 0, 0, c_fact_var);
}

I don't think there is much better here since the function requiring a *c_void type is fairly limiting. If there are stronger guarantees on the api (actually doesn't modify values) you could declare the function yourself with a *const c_void or manually use zig translate-c and modify as needed, then the ptrCast should work as expected.

The return value shouldn't matter here.

For completeness, note that casting a C function pointer to a void pointer is not necessarily defined, and therefore not portable. It is reasonable to call this required hack a bug in the C API.

OK, awesome. Thanks much for the solutions!

I ended up switching to D for my project, but have very much enjoyed my use of Zig and I think you guys are on to something. Very sorry to jump in, ask this question, then bounce. :sweat:

I'll likely be back at some point. I think that when craving more safety in systems programming, Zig is much more appealing than Rust. Keep it up. :+1: :bowing_man:

Was this page helpful?
0 / 5 - 0 ratings