Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call arbitrary function with argument struct #2930

Closed
daurnimator opened this issue Jul 22, 2019 · 8 comments
Closed

Call arbitrary function with argument struct #2930

daurnimator opened this issue Jul 22, 2019 · 8 comments
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@daurnimator
Copy link
Collaborator

I would like to be able to construct the arguments for a function and then call it.

e.g.

fn foo(x: u32, y: u8) u32 {
    return x+y;
}

fn callWith(comptime func: var, data: []const u8) u32 {
    assert(@typeInfo(func).Function.ReturnType == u32);
    const FuncArgs = @Arguments(func);
    var func_args = json.parseInto(FuncArgs, data);
    return @call(func, func_args);
}

Example use case:

@daurnimator
Copy link
Collaborator Author

daurnimator commented Jul 23, 2019

Example use case:

I managed to find a work around for a limited number of arguments:

        fn call(L: ?*lua.lua_State) (if (Fn.return_type) |rt| rt else noreturn) {
            if (Fn.args.len == 0) return @inlineCall(func);
            const a1 = check(L, 1, Fn.args[0].arg_type.?);
            if (Fn.args.len == 1) return @inlineCall(func, a1);
            const a2 = check(L, 2, Fn.args[1].arg_type.?);
            if (Fn.args.len == 2) return @inlineCall(func, a1, a2);
            const a3 = check(L, 3, Fn.args[2].arg_type.?);
            if (Fn.args.len == 3) return @inlineCall(func, a1, a2, a3);
            const a4 = check(L, 4, Fn.args[3].arg_type.?);
            if (Fn.args.len == 4) return @inlineCall(func, a1, a2, a3, a4);
            const a5 = check(L, 5, Fn.args[4].arg_type.?);
            if (Fn.args.len == 5) return @inlineCall(func, a1, a2, a3, a4, a5);
            const a6 = check(L, 6, Fn.args[5].arg_type.?);
            if (Fn.args.len == 6) return @inlineCall(func, a1, a2, a3, a4, a5, a6);
            const a7 = check(L, 7, Fn.args[6].arg_type.?);
            if (Fn.args.len == 7) return @inlineCall(func, a1, a2, a3, a4, a5, a6, a7);
            const a8 = check(L, 8, Fn.args[7].arg_type.?);
            if (Fn.args.len == 8) return @inlineCall(func, a1, a2, a3, a4, a5, a6, a7, a8);
            const a9 = check(L, 9, Fn.args[8].arg_type.?);
            if (Fn.args.len == 9) return @inlineCall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9);
            @compileError("NYI: >9 argument functions");
        }

@andrewrk andrewrk added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Jul 24, 2019
@andrewrk andrewrk added this to the 0.6.0 milestone Jul 24, 2019
@ghost ghost mentioned this issue Aug 8, 2019
@andrewrk
Copy link
Member

andrewrk commented Dec 9, 2019

This is now possible with @call. It works with tuple arguments, but not struct arguments yet. There is not an issue open for the latter yet, but the compile error you get points out that this is planned.

@andrewrk andrewrk closed this as completed Dec 9, 2019
@daurnimator
Copy link
Collaborator Author

daurnimator commented Dec 16, 2019

Is this expected?

    var args = .{};
    inline for (Fn.args) |arg, i| {
        const a = check(L, i+1, arg.arg_type.?);
        args = args ++ .{ a };
    }
    const result = @call(.{ .modifier = .always_inline }, func, args);
error: unable to evaluate constant expression
        args = args ++ .{ a };
                          ^

Similar if I write args = args ++ a; instead.

@daurnimator daurnimator reopened this Dec 22, 2019
@andrewrk andrewrk added the accepted This proposal is planned. label Feb 10, 2020
@andrewrk andrewrk modified the milestones: 0.6.0, 0.7.0 Feb 10, 2020
@andrewrk
Copy link
Member

Not sure why I closed this earlier without implementing it fully and adding tests.

Anyway #2930 (comment) looks like a separate issue having to do with either tuple concatenation, or the type of a comptime variable changing, or both.

@daurnimator
Copy link
Collaborator Author

daurnimator commented Mar 5, 2020

#4573 helped a little here, I was able to do a tail-recursive thing to remove the limit:

    return struct {
        // See https://github.com/ziglang/zig/issues/2930
        fn call(L: ?*lua.lua_State, args: var) (if (Fn.return_type) |rt| rt else void) {
            if (Fn.args.len == args.len) {
                return @call(.{}, func, args);
            } else {
                const i = args.len;
                const a = check(L, i + 1, Fn.args[i].arg_type.?);
                return @call(.{ .modifier = .always_inline }, call, .{ L, args ++ .{a} });
            }
        }

        fn thunk(L: ?*lua.lua_State) callconv(.C) c_int {
            const result = @call(.{ .modifier = .always_inline }, call, .{ L, .{} });
            if (@TypeOf(result) == void) {
                return 0;
            } else {
                push(L, result);
                return 1;
            }
        }
    }.thunk;

I'd prefer to be able to use the following instead:

            var args = .{};
            inline for (Fn.args) |arg, i| {
                var a = check(L, i + 1, arg.arg_type.?);
                args = args ++ .{a};
            }
            const result = @call(.{}, func, args);

Which now fails with:

./src/autolua.zig:225:29: error: expected type 'struct:222:25', found 'struct:225:29'
                args = args ++ .{a};
                            ^
./src/autolua.zig:222:25: note: struct:222:25 declared here
            var args = .{};
                        ^
./src/autolua.zig:225:29: note: struct:225:29 declared here
                args = args ++ .{a};
                            ^
./src/autolua.zig:225:29: error: expected type 'struct:222:25', found 'struct:225:29'
                args = args ++ .{a};
                            ^
./src/autolua.zig:222:25: note: struct:222:25 declared here
            var args = .{};
                        ^
./src/autolua.zig:225:29: note: struct:225:29 declared here
                args = args ++ .{a};
                            ^
./src/autolua.zig:225:29: error: expected type 'struct:222:25', found 'struct:225:29'
                args = args ++ .{a};
                            ^
./src/autolua.zig:222:25: note: struct:222:25 declared here
            var args = .{};
                        ^
./src/autolua.zig:225:29: note: struct:225:29 declared here
                args = args ++ .{a};
                            ^

But I understand that it is sort of mutating the type of args, so I'm not sure if its something zig wants to support?

@daurnimator
Copy link
Collaborator Author

daurnimator commented Aug 17, 2020

with thanks to @ifreund

const std = @import("std");

const ArgTypeMagic = struct {
    args: anytype,
};

fn ArgType(func: anytype) type {
    const Fn = @typeInfo(@TypeOf(func)).Fn;
    const tuple = .{};
    comptime var magic = ArgTypeMagic{ .args = tuple };
    inline for (Fn.args) |arg, i| {
        magic.args = magic.args ++ .{@as(arg.arg_type.?, undefined)};
    }
    return @TypeOf(magic.args);
}

fn bar(a: u32, b: i32) void {
    std.debug.print("{} {}\n", .{a, b});
}

pub fn main() anyerror!void {
    const T = ArgType(bar);
    @call(.{}, bar, T{1,2});
}

edit: trying to use this I get "cannot assign to constant"; so seems like a no-go.

@ifreund
Copy link
Member

ifreund commented Oct 13, 2020

I think we can close this now with #6415 merged. The following works:

const std = @import("std");

fn bar(a: [*:0]const u8, b: i32) void {
    std.debug.print("{} {}\n", .{ a, b });
}

pub fn main() anyerror!void {
    const T = std.meta.ArgsTuple(@TypeOf(bar));
    // argv[0] just to make sure the value is runtime
    @call(.{}, bar, T{ std.os.argv[0], 2 });
}

@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Oct 17, 2020
@daurnimator
Copy link
Collaborator Author

It worked! daurnimator/zig-autolua@3c71377

@Vexu Vexu modified the milestones: 0.8.0, 0.7.0 Oct 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

4 participants