I mixed it in quickly before I had to get back to work and produced something that seemed less stupid than my initial implementation.
const std = @import("std");
const ArrayList = std.ArrayList;
test {
var event = EventT1(i32).init(std.heap.page_allocator);
defer event.deinit();
const event1 = struct {
action: Action(i32, void) = .{ .compute = compute },
val: @Vector(3, f32),
fn compute(action: *Action(i32, void), arg: i32) void {
const self: *@This() = @alignCast(@fieldParentPtr("action", action));
std.debug.print("Value: {} and {}\n", .{ arg, self.val });
}
};
const event1s = event1{ .val = @Vector(3, f32){ 1.0, 2.0, 3.0 } };
try event.add(@constCast(&event1s.action));
event.invoke(64);
}
pub fn Action(comptime T: type, comptime R: type) type {
return struct {
compute: *const fn (*Action(T, R), T) R,
};
}
pub fn EventT1(comptime _T: type) type {
return struct {
invokes: List,
pub const T = _T;
const List = ArrayList(*Action(T, void));
const Self = @This();
pub inline fn init(allocator: std.mem.Allocator) Self {
return Self{ .invokes = ArrayList(*Action(T, void)).init(allocator) };
}
pub inline fn add(self: *Self, callable: *Action(T, void)) !void {
try self.invokes.append(callable);
}
pub inline fn addSlice(self: *Self, callables: []const *Action(T, void)) !void {
try self.invokes.appendSlice(callables);
}
pub inline fn clear(self: *Self) void {
self.invokes.clearAndFree();
}
pub inline fn invoke(self: *Self, arg: T) void {
for (self.invokes.items) |callable| {
callable.compute(callable, arg);
}
}
pub inline fn deinit(self: *Self) void {
self.invokes.clearAndFree();
self.invokes.deinit();
}
};
}