Created
June 15, 2020 19:04
-
-
Save adrusi/c32e8133914fab5800d0eea7e3d6be15 to your computer and use it in GitHub Desktop.
Userland implementation of closures in Zig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const assert = std.debug.assert; | |
pub fn closure(bindings: var) ClosureInternal(@TypeOf(bindings)) { | |
return ClosureInternal(@TypeOf(bindings)) { .ctx = bindings }; | |
} | |
fn ClosureInternal(comptime Spec: type) type { | |
comptime { | |
const spec_tinfo = @typeInfo(Spec); | |
assert(spec_tinfo == .Struct); | |
for (spec_tinfo.Struct.fields) |field| { | |
assert(field.default_value == null); | |
} | |
assert(spec_tinfo.Struct.decls.len == 1); | |
const call_decl = spec_tinfo.Struct.decls[0]; | |
assert(call_decl.is_pub); | |
assert(std.mem.eql(u8, call_decl.name, "call")); | |
const call = Spec.call; | |
const call_tinfo = @typeInfo(@TypeOf(call)); | |
assert(call_tinfo == .Fn); | |
assert(!call_tinfo.Fn.is_generic); | |
assert(call_tinfo.Fn.args.len >= 1); | |
assert(call_tinfo.Fn.args[0].arg_type.? == *const Spec); | |
var arg_types: [call_tinfo.Fn.args.len - 1]type = undefined; | |
for (call_tinfo.Fn.args[1..]) |arg, i| { | |
arg_types[i] = arg.arg_type.?; | |
} | |
const RetType = call_tinfo.Fn.return_type.?; | |
return Closure(Spec, arg_types[0..], RetType); | |
} | |
} | |
pub fn Closure(comptime Ctx: type, comptime arg_types: []type, comptime RetType: type) type { | |
return struct { | |
ctx: Ctx, | |
pub fn call(self: *const @This(), args: var) RetType { | |
comptime { | |
assert(args.len == arg_types.len); | |
for (args) |_, i| { | |
assert(@TypeOf(args[i]) == arg_types[i]); | |
} | |
} | |
return @call(.{}, Ctx.call, .{ &self.ctx } ++ args); | |
} | |
}; | |
} | |
test "closures" { | |
var x: i32 = 60; | |
const foo = closure(struct { | |
x: *i32, | |
pub fn call(self: *const @This(), y: i32) i32 { | |
self.x.* += y; | |
return 420; | |
} | |
} { .x = &x }); | |
assert(foo.call(.{ @as(i32, 9) }) == 420); | |
assert(x == 69); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, thank you for sharing this. To help more people, I updated this code to make it compile under Zig 0.11. Following is a diff patch