85 lines
2.8 KiB
Zig
85 lines
2.8 KiB
Zig
///
|
|
/// 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,
|
|
};
|
|
}
|
|
};
|
|
}
|