90 lines
2.4 KiB
Zig
90 lines
2.4 KiB
Zig
const coral = @import("./coral.zig");
|
|
|
|
const std = @import("std");
|
|
|
|
destroy: *const fn (*anyopaque) void,
|
|
erased: *anyopaque,
|
|
|
|
fn Layout(comptime Type: type) type {
|
|
return switch (Type) {
|
|
void => struct {
|
|
fn destroy(_: *anyopaque) void {}
|
|
},
|
|
|
|
else => struct {
|
|
allocator: std.mem.Allocator,
|
|
value: Type,
|
|
|
|
const TypeLayout = @This();
|
|
|
|
fn destroy(erased: *anyopaque) void {
|
|
const layout: *TypeLayout = @ptrCast(@alignCast(erased));
|
|
|
|
if (@hasDecl(Type, "deinit")) {
|
|
const deinit_fn = switch (@typeInfo(@TypeOf(Type.deinit))) {
|
|
.@"fn" => |@"fn"| @"fn",
|
|
|
|
else => @compileError(std.fmt.comptimePrint("Declaration `{s}.deinit` must be an fn type to be boxable", .{
|
|
@typeName(Type),
|
|
})),
|
|
};
|
|
|
|
if (deinit_fn.params.len != 1 or deinit_fn.params[0].type != *Type) {
|
|
@compileError(std.fmt.comptimePrint("Fn `{s}.deinit` is only permitted 1 parameter to be boxable and it must be of type {s}", .{
|
|
@typeName(Type),
|
|
@typeName(*Type),
|
|
}));
|
|
}
|
|
|
|
layout.value.deinit();
|
|
}
|
|
|
|
layout.allocator.destroy(layout);
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
const Self = @This();
|
|
|
|
pub fn deinit(self: *Self) void {
|
|
self.destroy(self.erased);
|
|
|
|
self.* = undefined;
|
|
}
|
|
|
|
pub fn has(self: Self, comptime Value: type) ?*Value {
|
|
const ValueLayout = Layout(Value);
|
|
|
|
if (self.destroy == ValueLayout.destroy) {
|
|
const layout: *ValueLayout = @ptrCast(@alignCast(self.erased));
|
|
|
|
return &layout.value;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
pub fn init(allocator: std.mem.Allocator, value: anytype) std.mem.Allocator.Error!Self {
|
|
const Value = @TypeOf(value);
|
|
const ValueLayout = Layout(Value);
|
|
const value_layout = try allocator.create(ValueLayout);
|
|
|
|
errdefer {
|
|
allocator.destroy(value_layout);
|
|
}
|
|
|
|
if (@hasField(ValueLayout, "allocator")) {
|
|
value_layout.allocator = allocator;
|
|
}
|
|
|
|
if (@hasField(ValueLayout, "value")) {
|
|
value_layout.value = value;
|
|
}
|
|
|
|
return .{
|
|
.erased = @ptrCast(value_layout),
|
|
.destroy = ValueLayout.destroy,
|
|
};
|
|
}
|