Hello. Standard disclaimer about first time post / non-professional Zig dev here.
(code is written for 0.16)
Recently while tinkering around I came across a situation where I wanted to be able to pass in function pointers to another function (it was a testbench for some algorithms, not really too relevant here though) that had varying layouts in their function signatures.
I tried first having something like this (I unfortunately wasn’t committing diligently enough so I don’t have what I tried exactly):
const TestSignature = struct {
comptime FnType: type, //signature for the function pointer
fn: FnType,
parameters: ParameterLayout,
}
const ParameterLayout = enum {
LayoutOne,
LayoutTwo,
}
The thought was that I could switch on the enum to actually pass the correct arguments to the function pointer. I wasn’t able to get this to work, so throwing together a different approach, I came up with this:
const std = @import("std");
pub fn main(init: std.process.Init) void {
_ = init;
const c1: contextStruct = .{ .fnPtr = &a, .mapInfo = .LayoutOne };
const c2: contextStruct = .{ .fnPtr = &b, .mapInfo = .LayoutTwo };
acceptsCtx(c1);
acceptsCtx(c2);
}
fn a() void {
std.debug.print("Hi\n", .{});
}
fn b(x: u8) u8 {
return x % 5;
}
const mapEnum = enum { LayoutOne, LayoutTwo };
const contextStruct = struct {
fnPtr: *const anyopaque,
mapInfo: mapEnum,
};
inline fn mapHelper (m: mapEnum) type {
return switch (m) {
.LayoutOne => *const fn () void,
.LayoutTwo => *const fn (u8) u8,
};
}
fn acceptsCtx(comptime ctx: contextStruct) void {
switch (ctx.mapInfo) {
.LayoutOne => {
const type_info = mapHelper(ctx.mapInfo);
const casted: type_info = @ptrCast(ctx.fnPtr);
casted();
},
.LayoutTwo => {
const type_info = mapHelper(ctx.mapInfo);
const casted: type_info = @ptrCast(ctx.fnPtr);
std.debug.print("{d}\n", .{casted(5)});
},
}
}
While this seems to work, I was wondering if there were better or more advisable ways of doing something like this.
My two main questions are:
- Is there a safer / more elegant / more idiomatic way of supporting something like this? What have other people done?
- I admit to not really understanding anyopaque well from the main documentation, and it was kind of just through trial and error that I got to this working version. Could someone explain the concept behind it or point me towards documentation I might have been missing on it?
(Bonus points) If this kind of function wrangling isn’t really advisable for some reason I’m not aware of do let me know and why that would be. ![]()
Thanks a bunch for taking the time to help me along.