mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 23:24:09 +00:00
WIP: progress on Bound function
This commit is contained in:
parent
9d3434c21c
commit
3353b6a525
1 changed files with 130 additions and 18 deletions
|
@ -1,36 +1,148 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
// attempt 1: explicitly typed
|
/// Bind a function with specific signature to a method of any instance of a given type
|
||||||
pub fn Create(Fn: type, Instance: type) type {
|
pub fn OldBind(func: anytype, instance: anytype) OldBound(@typeInfo(@TypeOf(instance)).pointer.child, func) {
|
||||||
|
const Instance = @typeInfo(@TypeOf(instance)).pointer.child;
|
||||||
|
return OldBound(Instance, func).init(@constCast(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn OldBound(Instance: type, func: anytype) type {
|
||||||
|
// Verify Func is a function type
|
||||||
|
const func_info = @typeInfo(@TypeOf(func));
|
||||||
|
if (func_info != .@"fn") {
|
||||||
|
@compileError("OldBound expexts a function type as second parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify first parameter is pointer to Instance type
|
||||||
|
const params = func_info.@"fn".params;
|
||||||
|
if (params.len == 0 or (params[0].type != *Instance and params[0].type != *const Instance)) {
|
||||||
|
@compileError("Function's first parameter must be " ++ @typeName(Instance) ++ " but got: " ++ @typeName(params[0].type.?));
|
||||||
|
}
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
instance: *Instance,
|
instance: *Instance,
|
||||||
function: *const Fn,
|
|
||||||
|
const OldBoundFunction = @This();
|
||||||
|
|
||||||
|
pub fn call(self: OldBoundFunction, args: anytype) func_info.@"fn".return_type.? {
|
||||||
|
return @call(.auto, func, .{self.instance} ++ args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convenience init
|
||||||
|
pub fn init(instance_: *Instance) OldBoundFunction {
|
||||||
|
return .{ .instance = instance_ };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test "OldBound" {
|
||||||
|
const Person = struct {
|
||||||
|
name: []const u8,
|
||||||
|
_buf: [1024]u8 = undefined,
|
||||||
|
|
||||||
|
// takes const instance
|
||||||
|
pub fn greet(self: *const @This(), gpa: std.mem.Allocator, greeting: []const u8) ![]const u8 {
|
||||||
|
return std.fmt.allocPrint(gpa, "{s}, {s}!\n", .{ greeting, self.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes non-const instance
|
||||||
|
pub fn farewell(self: *@This(), message: []const u8) ![]const u8 {
|
||||||
|
return std.fmt.bufPrint(self._buf[0..], "{s}, {s}!\n", .{ message, self.name });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var alice: Person = .{ .name = "Alice" };
|
||||||
|
|
||||||
|
// creation variant a: manually instantiate
|
||||||
|
const bound_greet: OldBound(Person, Person.greet) = .{ .instance = &alice };
|
||||||
|
|
||||||
|
// creation variant b: call init function
|
||||||
|
const bound_farewell = OldBound(Person, Person.farewell).init(&alice);
|
||||||
|
|
||||||
|
const ta = std.testing.allocator;
|
||||||
|
const greeting = try bound_greet.call(.{ ta, "Hello" });
|
||||||
|
defer ta.free(greeting);
|
||||||
|
|
||||||
|
try std.testing.expectEqualStrings("Hello, Alice!\n", greeting);
|
||||||
|
try std.testing.expectEqualStrings("Goodbye, Alice!\n", try bound_farewell.call(.{"Goodbye"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
test OldBind {
|
||||||
|
const Person = struct {
|
||||||
|
name: []const u8,
|
||||||
|
_buf: [1024]u8 = undefined,
|
||||||
|
|
||||||
|
// takes const instance
|
||||||
|
pub fn greet(self: *const @This(), gpa: std.mem.Allocator, greeting: []const u8) ![]const u8 {
|
||||||
|
return std.fmt.allocPrint(gpa, "{s}, {s}!\n", .{ greeting, self.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes non-const instance
|
||||||
|
pub fn farewell(self: *@This(), message: []const u8) ![]const u8 {
|
||||||
|
return std.fmt.bufPrint(self._buf[0..], "{s}, {s}!\n", .{ message, self.name });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var alice: Person = .{ .name = "Alice" };
|
||||||
|
|
||||||
|
const bound_greet = OldBind(Person.greet, &alice);
|
||||||
|
const bound_farewell = OldBind(Person.farewell, &alice);
|
||||||
|
|
||||||
|
const ta = std.testing.allocator;
|
||||||
|
const greeting = try bound_greet.call(.{ ta, "Hello" });
|
||||||
|
defer ta.free(greeting);
|
||||||
|
|
||||||
|
try std.testing.expectEqualStrings("Hello, Alice!\n", greeting);
|
||||||
|
try std.testing.expectEqualStrings("Goodbye, Alice!\n", try bound_farewell.call(.{"Goodbye"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind functions like `fn(a: X, b: Y)` to an instance of a struct. When called, the instance's `pub fn(self: *This(), a: X, b: Y)` is called.
|
||||||
|
///
|
||||||
|
/// make callbacks stateful when they're not meant to be?
|
||||||
|
// pub fn Bound(Instance: type, Func: type, func: anytype) type {
|
||||||
|
pub fn Bound(Instance: type, Func: type, DFunc: type) type {
|
||||||
|
|
||||||
|
// TODO: construct DFunc on-the-fly
|
||||||
|
|
||||||
|
// Verify Func is a function type
|
||||||
|
const func_info = @typeInfo(Func);
|
||||||
|
// if (func_info != .@"fn") {
|
||||||
|
// @compileError("Bound expexts a function type as second parameter");
|
||||||
|
// }
|
||||||
|
return struct {
|
||||||
|
instance: *Instance,
|
||||||
|
foo: *const DFunc,
|
||||||
|
|
||||||
const BoundFunction = @This();
|
const BoundFunction = @This();
|
||||||
|
|
||||||
pub fn init(function: *const Fn, instance: *Instance) BoundFunction {
|
pub fn call(self: BoundFunction, args: anytype) func_info.@"fn".return_type.? {
|
||||||
return .{
|
return @call(.auto, self.foo, .{self.instance} ++ args);
|
||||||
.instance = instance,
|
|
||||||
.function = function,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(self: *const BoundFunction, arg: anytype) void {
|
// convenience init
|
||||||
@call(.auto, self.function, .{ self.instance, arg });
|
pub fn init(instance_: *Instance, foo_: *const DFunc) BoundFunction {
|
||||||
|
return .{ .instance = instance_, .foo = foo_ };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
test "BoundFunction" {
|
test Bound {
|
||||||
const X = struct {
|
const Person = struct {
|
||||||
field: usize = 0,
|
name: []const u8,
|
||||||
pub fn foo(self: *@This(), other: usize) void {
|
_buf: [1024]u8 = undefined,
|
||||||
std.debug.print("field={d}, other={d}\n", .{ self.field, other });
|
|
||||||
|
pub fn speak(self: *@This(), message: []const u8) ![]const u8 {
|
||||||
|
return std.fmt.bufPrint(self._buf[0..], "{s} says: >>{s}!<<\n", .{ self.name, message });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var x: X = .{ .field = 27 };
|
const CallBack = fn ([]const u8) anyerror![]const u8;
|
||||||
|
|
||||||
var bound = Create(@TypeOf(X.foo), X).init(X.foo, &x);
|
var alice: Person = .{ .name = "Alice" };
|
||||||
bound.call(3);
|
|
||||||
|
const bound_greet = Bound(Person, CallBack, @TypeOf(Person.speak)).init(&alice, &Person.speak);
|
||||||
|
|
||||||
|
const greeting = try bound_greet.call(.{"Hello"});
|
||||||
|
|
||||||
|
try std.testing.expectEqualStrings("Alice says: >>Hello!<<\n", greeting);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue