Can you explain what you mean when you say it’s not ergonomic?
What I mean is that it is annoying having to @ptrFromInt
or @ptrCast
the argument at the beginning of all my callback functions. It also feels unsafe.
Maybe it’s best if I give you an example of what I currently have and what I want to have:
// Just a random example:
// When I click the button it should open the world with the given name
fn openWorld(namePtr: usize) void {
const nullTerminatedName: [*:0]const u8 = @ptrFromInt(namePtr);
...
// When creating the button I give it a callback with function ptr and name:
Button.initText(..., .{.callback = &openWorld, .arg = @intFromPtr(name.ptr)})
Now instead I would like to do something like this:
fn openWorld(nullTerminatedName: [*:0]const u8) void {
...
// When creating the button I give it a callback with function ptr and name:
Button.initText(..., Callback.init(&openWorld, name.ptr))
Here Callback.init
does the function pointer cast and would also do some safety checks, making sure that @TypeOf(name.ptr)
and @TypeOf(meta.ArgsTuple(openWorld)[0])
match.
To me this would be more ergonomic because I don’t need to do any casting(only once inside the init function), and I’m probably even safer because I cannot mess up by for example accidently passing a non-terminated pointer.
this approach is implementation defined for C code.
I only want to use this inside of my zig project. Is this undefined behavior for Zig as well?
Now, let’s say x is an *anyopaque (or for the C folks, a good ol’ fashioned void pointer). I would fully anticipate that foo is going to cast that argument at some point. Like… almost no doubt.
I agree, but currently the majority of use-cases just store a number(like an index into some list) in the callback. To me it just feels wrong needing to cast it to a pointer when creating the callback. And honestly I’m a bit afraid that some future null safety check would screw me over if I did.