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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user