/// /// 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 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 In: type, comptime Out: type) type { return struct { callErased: fn (*anyopaque, In) Out, context: *anyopaque, fn Invoker(comptime Context: type) type { return if (Context == void) fn (In) Out else fn (Context, In) Out; } /// /// Function type. /// const Self = @This(); /// /// Invokes `self` with `input`, producing a result according to the current context data. /// pub fn call(self: Self, input: In) Out { return self.callErased(self.context, input); } /// /// Creates and returns a [Self] using the `invoke` as the behavior executed when [call] or /// [callErased] is called. /// /// For creating a closure-style function, see [fromClosure]. /// pub fn from(comptime invoke: fn (In) Out) Self { return .{ .context = undefined, .callErased = struct { fn callErased(_: *anyopaque, input: In) Out { return invoke(input); } }.callErased, }; } /// /// Creates and returns a [Self] by capturing the `context` value as the capture context and /// `invoke` as the behavior executed when [call] or [callErased] is called. /// /// The newly created [Self] is returned. /// pub fn fromClosure(context: anytype, comptime invoke: fn (@TypeOf(context), In) Out) Self { const Context = @TypeOf(context); switch (@typeInfo(Context)) { .Pointer => |info| if (info.size == .Slice) @compileError("`context` cannot be a slice"), .Void => {}, else => @compileError("`context` must be a pointer"), } return Self{ .context = @ptrCast(*anyopaque, context), .callErased = struct { fn callErased(erased: *anyopaque, input: In) Out { return if (Context == void) invoke(input) else invoke(@ptrCast( Context, @alignCast(@alignOf(Context), erased)), input); } }.callErased, }; } }; }