Zig NEWS

Cover image for Function Tables
David Vanderson
David Vanderson

Posted on

Function Tables

Jean-Pierre follows up with a common gui situation - a combobox dropdown that lists some available functions. When the user clicks an entry, the code is given an enum value.

How can you use an enum to select which function to run?

This can trip up people especially if they are used to languages like Javascript or Racket where functions are first-class constructs. You can put all the functions in a list, index into the list and call the function.

Can we do that in Zig? Yes, as long as all the functions have the same type:

const std = @import("std");

var count: i32 = 0;

pub fn printFn() void {
    std.debug.print("count is {d}\n", .{count});
}

pub fn incrementFn() void {
    count += 1;
}

pub const FnEnum = enum {
    print,
    increment,

    pub const Table = [@typeInfo(FnEnum).Enum.fields.len]*const fn () void{
        printFn,
        incrementFn,
    };
};

pub fn main() !void {
    var input: FnEnum = .print; // User clicks "print"
    FnEnum.Table[@enumToInt(input)]();

    input = .increment; // User clicks "increment"
    FnEnum.Table[@enumToInt(input)]();

    input = .print; // User clicks "print" again
    FnEnum.Table[@enumToInt(input)]();
}
Enter fullscreen mode Exit fullscreen mode

What if the functions have different types?

I would manually switch on the enum for each function:

const std = @import("std");

var count: i32 = 0;

pub fn printFn() void {
    std.debug.print("count is {d}\n", .{count});
}

pub fn incrementFn(v: *i32) void {
    v.* += 1;
}

pub const FnEnum = enum {
    print,
    increment,

    pub fn run(self: FnEnum) void {
        switch (self) {
            .print => printFn(),
            .increment => _ = incrementFn(&count),
        }
    }
};

pub fn main() !void {
    var input: FnEnum = .print; // User clicks "print"
    input.run();

    input = .increment; // User clicks "increment"
    input.run();

    input = .print; // User clicks "print" again
    input.run();
}
Enter fullscreen mode Exit fullscreen mode

Hope this helps!

Top comments (3)

Collapse
 
lhp profile image
Leon Henrik Plickat

You can put all the functions in a list, index into the list and call the function.

You should be able to put the functions in a struct and index into @typeInfo(impl).Struct.decls[i]. Also you could use a comptime [enum tag <-> function pointer] map, or compare @tagName against decls[i].name.

Although I personally can't think of any case right now where I'd not just use an enum plus a switch. It's a lot more straight-forward.

Maybe we'll have anonymous functions functions in the future. Perhaps then using a union(enum) of functions could be interesting, if only to avoid having to keep the enum, the action implementation and the matching code in sync.

Collapse
 
jpl profile image
Jean-Pierre

thank you, the second solution is that

Very interesting to make software whose function call is encapsulated in fields for example, we click on the country, and we must display the function which offers to choose the country

Collapse
 
jpl profile image
Jean-Pierre

Thanks a lot