Adding type assertions in external C lib bindings?

I’m making Zig bindings to the Nix evaluator, and I have a function type that I need to pass that has this C signature:

typedef void(* nix_get_string_callback) (const char *start, unsigned int n, void *user_data);

Which gets translated to this after I @cImport the header containing this definition:

const nix_get_string_callback = ?*const fn ([*c]const u8, c_uint, ?*anyopaque) callconv(.C) void

I don’t quite know how I would be able to use this; I’m able to define a function with the same signature, but I don’t know how to cast the ?*anyopaque pointer to the C type that I need (which would be [*c]const u8) in order to dereference it, since you can’t dereference an *anyopaque. How should I go about implementing a function with this signature?

Not quite sure it will be helpful, but I am using little util function which looks like this:

pub fn opaqPtrTo(ptr: ?*anyopaque, comptime T: type) T {
    return @ptrCast(@alignCast(ptr));
}

and then everywhere when I need to cast ?*anyopaque to something special:

// me.data is ?*anyopaque, WorkerData is some struct
var wd = util.opaqPtrTo(me.data, *WorkerData);

Ah yeah I tried this, but when I tried to @alignCast the resulting pointer, it detected incorrect alignment coming from the cast and panicked.

The original function I tried to make to just assign the opaque pointer some data coming from a C string was this:

fn get_unowned_string_callback(str: [*c]const u8, len: c_uint, data: ?*anyopaque) callconv(.C) void {
    _ = len;
    const output_ptr: [*c][*c]const u8 = @ptrCast(@alignCast(data));
    output_ptr.* = str;
}

Is there a way I can do this while preserving the correct alignment? The type of the pointer I want to assign it to is a char** or [*c][*c]const u8 on the C side.

Shouldn’t it be just [*c]const u8? What are you passing to get_unowned_string_callback() as a 3rd parameter?

BTW, here is written that a c-pointer, in particular:

  • Does not support Zig-only pointer attributes such as alignment. Use normal Pointers please!

Shouldn’t it be just [*c]const u8? What are you passing to get_unowned_string_callback() as a 3rd parameter?

I thought so too at first, but the pointer I’m passing to this function is a way of retrieving a const char* pointer. I should probably provide more context on why I’m doing this.

The function that I need to pass this function to is this:

nix_err nix_get_string(nix_c_context* context, const Value* value, nix_get_string_callback, callback, void* user_data); 	

Where I’m attempting to retrieve the value that was given to me by the Nix evaluator (stored in the *start param) and put its value into the *user_data variable, unmodified. Seeing that Zig alignment is not possible with C pointers (makes sense), though, does that mean that I can’t cast an ?*anyopaque pointer to a C-style [*c] pointer at all, and I should just drop into C code to write this callback or something like that?

Since you pass char** ([*c][*c]u8) to void* (?*anyopaque) the following must work:

export fn callback(start: [*c]const u8, n: c_uint, data: ?*align(@alignOf([*][*]u8)) anyopaque) callconv(.C) void {
    const ptr: [*c][*c]u8 = @ptrCast(@alignCast(data));
    ...
}

Sorry for not responding to this for a while. I upgraded my project to use 0.12.0 from 0.11.0 recently, and finally came up with the following solution to my problem based on the help given here:

const cstr = @cImport({
    @cInclude("string.h");
});

fn owned_string_callback(start: [*c]const u8, n: c_uint, data: ?*anyopaque) callconv(.C) void {
    _ = n;
    const ptr: [*c][*c]u8 = @ptrCast(@alignCast(data));
    ptr.* = cstr.strdup(start);
}

And used it like so:

var buf: [*c]u8 = undefined;
const err = libnix.nix_get_string(context.context, self.value, owned_string_callback, @ptrCast(&buf));

I guess it was just not playing well with 0.11.0 for some weird reason. Sorry for not providing enough context initially, and thanks for the help! I appreciate it :}

1 Like