From 2e544393a5df156ebc63b2cbde81fc1deafc8ce6 Mon Sep 17 00:00:00 2001 From: kayomn Date: Wed, 2 Nov 2022 13:11:17 +0000 Subject: [PATCH] Simplify programming interface for creating closureless Functions --- src/core/io.zig | 18 +++++++----------- src/core/meta.zig | 33 ++++++++++++++++++++++++++++----- src/core/stack.zig | 2 +- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/core/io.zig b/src/core/io.zig index be0c92b..43a3698 100644 --- a/src/core/io.zig +++ b/src/core/io.zig @@ -377,23 +377,19 @@ test "Data swapping" { } /// -/// Returns a [Writer] that silently consumes all given data without failure and throws it away. +/// [Writer] that silently consumes all given data without failure and throws it away. /// /// This is commonly used for testing or redirected otherwise unwanted output data that has to be /// sent somewhere for whatever reason. /// -pub fn nullWriter() Writer { - var dummy: usize = 0; - - return Writer.capture(&dummy, struct { - fn write(_: *usize, buffer: []const u8) usize { - return buffer.len; - } - }.write); -} +pub const null_writer = Writer.from(struct { + fn write(buffer: []const u8) usize { + return buffer.len; + } +}.write); test "Null writing" { const sequence = "foo"; - try testing.expect(nullWriter().call(sequence) == sequence.len); + try testing.expect(null_writer.call(sequence) == sequence.len); } diff --git a/src/core/meta.zig b/src/core/meta.zig index e176c1b..cbbc534 100644 --- a/src/core/meta.zig +++ b/src/core/meta.zig @@ -18,6 +18,10 @@ pub fn Function(comptime In: type, comptime Out: type) type { 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. /// @@ -26,23 +30,42 @@ pub fn Function(comptime In: type, comptime Out: type) type { /// /// Invokes `self` with `input`, producing a result according to the current context data. /// - pub fn call(self: *Self, input: In) Out { + pub fn call(self: Self, input: In) Out { return self.callErased(self.context, input); } /// - /// Creates a new [Self] by capturing the `context` value as the capture context and + /// 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 capture(context: anytype, comptime invoke: fn (@TypeOf(context), In) Out) Self { + 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"), } @@ -51,8 +74,8 @@ pub fn Function(comptime In: type, comptime Out: type) type { .callErased = struct { fn callErased(erased: *anyopaque, input: In) Out { - return invoke(@ptrCast(*Context, @alignCast( - @alignOf(Context), erased)).*, input); + return if (Context == void) invoke(input) else invoke(@ptrCast( + *Context, @alignCast(@alignOf(Context), erased)).*, input); } }.callErased, }; diff --git a/src/core/stack.zig b/src/core/stack.zig index 98c5c4b..841f76f 100755 --- a/src/core/stack.zig +++ b/src/core/stack.zig @@ -122,7 +122,7 @@ pub const PushError = io.Allocator.MakeError; /// referenced by `fixed_stack` until it is full. /// pub fn fixedWriter(fixed_stack: *Fixed(u8)) io.Writer { - return io.Writer.capture(fixed_stack, struct { + return io.Writer.fromClosure(fixed_stack, struct { fn write(stack: *Fixed(u8), buffer: []const u8) usize { stack.pushAll(buffer) catch |err| switch (err) { error.OutOfMemory => return 0,