const std = @import("std"); /// /// Returns the return type of the function type `Fn`. /// pub fn FnReturn(comptime Fn: type) type { const type_info = @typeInfo(Fn); if (type_info != .Fn) @compileError("`Fn` must be a function type"); return type_info.Fn.return_type orelse void; } /// /// Returns a double-input single-output closure type where `A` represents the first input type, `B` /// represents the second, and `Out` represents the output type, and `captures_size` represents the /// size of the closure context. /// pub fn BiFunction(comptime captures_size: usize, comptime A: type, comptime B: type, comptime Out: type) type { return struct { applyErased: fn (*anyopaque, A, B) Out, context: [captures_size]u8, /// /// Function type. /// const Self = @This(); /// /// Applies `a` and `b` to `self`, producing a result according to the current context data. /// pub fn apply(self: *Self, a: A, b: B) Out { return self.applyErased(&self.context, a, b); } /// /// Creates a new [Self] by capturing the `captures` value as the context and `call` as the /// as the behavior executed when [apply] or [applyErased] is called. /// /// The newly created [Self] is returned. /// pub fn capture(captures: anytype, comptime call: fn (@TypeOf(captures), A, B) Out) Self { const Captures = @TypeOf(captures); if (@sizeOf(Captures) > captures_size) @compileError("`captures` must be smaller than or equal to " ++ std.fmt.comptimePrint("{d}", .{captures_size}) ++ " bytes"); var function = Self{ .context = undefined, .applyErased = struct { fn applyErased(erased: *anyopaque, a: A, b: B) Out { return call(if (Captures == void) {} else @ptrCast(*Captures, @alignCast(@alignOf(Captures), erased)).*, a, b); } }.applyErased, }; if (captures != {}) { @ptrCast(*Captures, @alignCast(@alignOf(Captures), &function.context)).* = captures; } return function; } }; } /// /// Returns a single-input single-output closure type where `In` represents the input type, `Out` /// represents the output type, and `captures_size` represents the size of the closure context. /// pub fn Function(comptime captures_size: usize, comptime In: type, comptime Out: type) type { return struct { applyErased: fn (*anyopaque, In) Out, context: [captures_size]u8, /// /// Function type. /// const Self = @This(); /// /// Applies `input` to `self`, producing a result according to the current context data. /// pub fn apply(self: *Self, input: In) Out { return self.applyErased(&self.context, input); } /// /// Creates a new [Self] by capturing the `captures` value as the context and `call` as the /// as the behavior executed when [apply] or [applyErased] is called. /// /// The newly created [Self] is returned. /// pub fn capture(captures: anytype, comptime call: fn (@TypeOf(captures), In) Out) Self { const Captures = @TypeOf(captures); if (@sizeOf(Captures) > captures_size) @compileError("`captures` must be smaller than or equal to " ++ std.fmt.comptimePrint("{d}", .{captures_size}) ++ " bytes"); const captures_align = @alignOf(Captures); var function = Self{ .context = undefined, .applyErased = struct { fn applyErased(erased: *anyopaque, input: In) Out { return call(if (Captures == void) {} else @ptrCast(*Captures, @alignCast(@alignOf(Captures), erased)).*, input); } }.applyErased, }; @ptrCast(*Captures, @alignCast(captures_align, &function.context)).* = captures; return function; } }; }