Void* (C) and *anyopaque (Zig)

You could do this:

const std = @import("std");

const StageMachine = struct {
    foo: i8,
};
const Context = struct {
    bar: i8,
};

fn idleM1(me: *StageMachine, data: ?*anyopaque) void {
    var ctx = @ptrCast(*Context, @alignCast(@alignOf(*Context), data));
    me.foo = ctx.bar;
}

// what if we do this instead?
// -> transfer the noisy looking stuff into a function using comptime
fn cptr(comptime T: type, data: ?*anyopaque) T {
    return @ptrCast(T, @alignCast(@alignOf(T), data));
}
fn idleM2(me: *StageMachine, data: ?*anyopaque) void {
    var ctx = cptr(*Context, data);
    me.foo = ctx.bar;
}

pub fn main() void {
    var stage = StageMachine{ .foo = 20 };
    var context = Context{ .bar = 57 };

    std.debug.print("before: {any}\n", .{stage.foo});
    // idleM1(&stage, &context);
    idleM2(&stage, &context);
    std.debug.print("after: {any}\n", .{stage.foo});
}

(I am assuming here that some other code handles nullpointer checking, or there aren’t nulls…)

Personally I like this because it is close to what you want, but it also mentions cptr which gives some context to the programmer “we do some kind of transformation on that type (first argument) and data (second argument) by calling function cptr”.

Instead of having one implicit conversion.

Additionally being explicit here is also good, because now we can have several different functions similar to cptr that do different things (for example check for null pointer and panic in debug builds, or similar things); instead of just having one predefined conversion, that you are stuck with, if it doesn’t match your particular use case.
I think with conversions it is quite likely that different users have different cases, where other things are convenient. IMHO it is best to not choose, if there are multiple “correct” choices and instead let the programmer specify explicitly.
And then have ways to make things less tedious with comptime / meta programming or other techniques.


Are there cases where you always want one thing and have the language exactly do that?
Yes.
But I think in those cases you might also have a different question of:
Do I want to program in some kind of DSL / custom language here? Would it be worth the effort?
Does the language have some other kind of extensibility / meta-programming / macros / etc. so that I can customize it towards my goal without creating a full new language?
What tradeoffs do I really care about and what are specific goals?
What are you gaining and losing by that approach?

2 Likes