Is there a way to get the function name from pointer

Is there a way to get the function name from pointer, similar to how @tagName works? Obviously this would only work on comptime values.

If not, could this be a possible feature?

fn foo1() void {}
fn foo2() void {}
fn foo3() void {}

const FnType = fn () void;

fn stuff() void {
    const funs = comptime [_]FnType {
        foo1,
        foo2,
        foo3,
    };

    inline for(funs) |fun| {
        @functionName(fun);  // <<< does something like this exist?
    }
}

I’m passing around functions as I’m building a parser combinator, it would be good to get the name of the parser when there is an error.

Hello @JordanHendersonMusic and welcome to ziggit :slight_smile:

No, it is not possible to get the function name from the function type.
The reason is that a function may be an anonymous function without name (although currently in zig there are no anonymous functions and the functions always have a name).


To get around this limitation you can have a struct with a name and a function.

Also it is possible to list all the functions of a namespace using @typeInfo(T).Struct.decls where T is the struct, and the resulting decls contains public named declarations.

Finally it is possible to lookup a public function by its name from a namespace, using @field(T, name) where T is the namespace, name is a string of the function name and @field returns the function value.

3 Likes

You can in a roundabout way. When you use a comptime function to define a struct type, the arguments given will end up in the type name:

const std = @import("std");

fn Useless(comptime arg: anytype) type {
    return struct {
        const stuff = arg;
    };
}

pub fn main() void {
    std.debug.print("{s}\n", .{@typeName(Useless(1234))});
    std.debug.print("{s}\n", .{@typeName(Useless(.{ .pierogi = 12 }))});
}
fname.Useless(1234)
fname.Useless(.{ .pierogi = 12 })

If you pass a function as an argument, the function name shows up:

pub fn main() void {
    const funs = comptime [_]FnType{
        foo1,
        foo2,
        foo3,
    };
    inline for (funs) |fun| {
        std.debug.print("{s}\n", .{@typeName(Useless(fun))});
    }
}
fname.Useless((function 'foo1'))
fname.Useless((function 'foo2'))
fname.Useless((function 'foo3'))

Now it’s just a matter of plucking it out.

2 Likes

This is essentially what a stack trace does. It takes a pointer to an address in code and finds the current function and all parent functions. You could check out and leverage the stack trace dumping code in std.debug for this. Note that it will only work if your binary still has debug symbols, you won’t be able to get the function name otherwise.

That being said, using a stack tracer is probably silly for what you’re wanting to do. Here’s one solution that might work for you:

const std = @import("std");

fn foo1() void {}
fn foo2() void {}
fn foo3() void {}

const FnType = struct {
    ptr: fn() void,
    name: []const u8,
    pub fn init(comptime container: anytype, comptime name: []const u8) FnType {
        return .{
            .ptr = @field(container, name),
            .name = name,
        };
    }
};


pub fn main() void {
    const funs = comptime [_]FnType {
        FnType.init(@This(), "foo1"),
        FnType.init(@This(), "foo2"),
        FnType.init(@This(), "foo3"),
    };

    inline for(funs) |fun| {
        std.log.info("{s}", .{fun.name});
    }
}

If you’re not able to modify FnType to include the name, you could create a global std.AutoHashMap(*const fn() void, []const u8) that saves a mapping between every function pointer/name.

3 Likes