I want to use a function pointer as a struct field to have a runtime choice of an executed method.
For example, I want this code to print 010
:
const std = @import("std");
fn Foo() type {
return struct {
const Self = @This();
const doThingFunc = fn (self: *Self) u1;
doThing: doThingFunc = Self.doZero,
fn doZero(self: *Self) u1 {
self.doThing = Self.doOne;
return 0;
}
fn doOne(self: *Self) u1 {
self.doThing = Self.doZero;
return 1;
}
};
}
pub fn main() void {
var foo = Foo(){};
std.debug.print("{d}{d}{d}", .{ foo.doThing(), foo.doThing(), foo.doThing() });
}
But I get this error instead, which makes no sense for me:
An error occurred:
example.zig:9:19: error: parameter of type '*example.Foo()' must be declared comptime
fn doZero(self: *Self) u1 {
^~~~~~~~~~~
What is a correct way to implement desired behavior with function pointers in Zig?
With const doThingFunc = fn (self: *Self) u1;
OP code has declared a function body type, not a function pointer type. In Zig function body types are comptime-known, but function pointer types may be runtime-known. The correct declaration is:
const doThingFunc = *const fn (self: *Self) u1;
There is a further problem in OP code: struct methods can be called using the dot syntax, but you can't call a struct method through a function pointer using the dot syntax.
You could solve this problem by including the missing arguments:
std.debug.print("{d}{d}{d}", .{ foo.doThing(&foo), foo.doThing(&foo), foo.doThing(&foo) });
But this seems to violate the spirit of OP code. Instead, create a public method that calls the function pointer internally. Here is a modified version of OP code that does that.
const std = @import("std");
fn Foo() type {
return struct {
const Self = @This();
const DoThingFunc = *const fn (self: *Self) u1;
do_thing: DoThingFunc = &Self.doZero,
pub fn doThing(self: *Self) u1 {
return self.do_thing(self);
}
fn doZero(self: *Self) u1 {
self.do_thing = &Self.doOne;
return 0;
}
fn doOne(self: *Self) u1 {
self.do_thing = &Self.doZero;
return 1;
}
};
}
pub fn main() void {
var foo = Foo(){};
std.debug.print("{d}{d}{d}\n",
.{ foo.doThing(), foo.doThing(), foo.doThing() });
}
Here is the output of the program:
$ ./func_pointers
010