Simplify programming interface for creating closureless Functions
This commit is contained in:
parent
eb4a758251
commit
2e544393a5
|
@ -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
|
/// This is commonly used for testing or redirected otherwise unwanted output data that has to be
|
||||||
/// sent somewhere for whatever reason.
|
/// sent somewhere for whatever reason.
|
||||||
///
|
///
|
||||||
pub fn nullWriter() Writer {
|
pub const null_writer = Writer.from(struct {
|
||||||
var dummy: usize = 0;
|
fn write(buffer: []const u8) usize {
|
||||||
|
return buffer.len;
|
||||||
return Writer.capture(&dummy, struct {
|
}
|
||||||
fn write(_: *usize, buffer: []const u8) usize {
|
}.write);
|
||||||
return buffer.len;
|
|
||||||
}
|
|
||||||
}.write);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "Null writing" {
|
test "Null writing" {
|
||||||
const sequence = "foo";
|
const sequence = "foo";
|
||||||
|
|
||||||
try testing.expect(nullWriter().call(sequence) == sequence.len);
|
try testing.expect(null_writer.call(sequence) == sequence.len);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,10 @@ pub fn Function(comptime In: type, comptime Out: type) type {
|
||||||
callErased: fn (*anyopaque, In) Out,
|
callErased: fn (*anyopaque, In) Out,
|
||||||
context: *anyopaque,
|
context: *anyopaque,
|
||||||
|
|
||||||
|
fn Invoker(comptime Context: type) type {
|
||||||
|
return if (Context == void) fn (In) Out else fn (Context, In) Out;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Function type.
|
/// 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.
|
/// 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);
|
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.
|
/// `invoke` as the behavior executed when [call] or [callErased] is called.
|
||||||
///
|
///
|
||||||
/// The newly created [Self] is returned.
|
/// 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);
|
const Context = @TypeOf(context);
|
||||||
|
|
||||||
switch (@typeInfo(Context)) {
|
switch (@typeInfo(Context)) {
|
||||||
.Pointer => |info| if (info.size == .Slice)
|
.Pointer => |info| if (info.size == .Slice)
|
||||||
@compileError("`context` cannot be a slice"),
|
@compileError("`context` cannot be a slice"),
|
||||||
|
|
||||||
|
.Void => {},
|
||||||
else => @compileError("`context` must be a pointer"),
|
else => @compileError("`context` must be a pointer"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +74,8 @@ pub fn Function(comptime In: type, comptime Out: type) type {
|
||||||
|
|
||||||
.callErased = struct {
|
.callErased = struct {
|
||||||
fn callErased(erased: *anyopaque, input: In) Out {
|
fn callErased(erased: *anyopaque, input: In) Out {
|
||||||
return invoke(@ptrCast(*Context, @alignCast(
|
return if (Context == void) invoke(input) else invoke(@ptrCast(
|
||||||
@alignOf(Context), erased)).*, input);
|
*Context, @alignCast(@alignOf(Context), erased)).*, input);
|
||||||
}
|
}
|
||||||
}.callErased,
|
}.callErased,
|
||||||
};
|
};
|
||||||
|
|
|
@ -122,7 +122,7 @@ pub const PushError = io.Allocator.MakeError;
|
||||||
/// referenced by `fixed_stack` until it is full.
|
/// referenced by `fixed_stack` until it is full.
|
||||||
///
|
///
|
||||||
pub fn fixedWriter(fixed_stack: *Fixed(u8)) io.Writer {
|
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 {
|
fn write(stack: *Fixed(u8), buffer: []const u8) usize {
|
||||||
stack.pushAll(buffer) catch |err| switch (err) {
|
stack.pushAll(buffer) catch |err| switch (err) {
|
||||||
error.OutOfMemory => return 0,
|
error.OutOfMemory => return 0,
|
||||||
|
|
Loading…
Reference in New Issue