f96df2aed73d15cff5861f052175c1aee7f8f9de
zig interface helper
first why does this exist i didnt like having to write *anyopaque for all the functions that are in the implemention of an interface so i made wrapper do it for you. This now hides all the nasty polymorphism shenanigans within the interface where it belongs.
Additionally it also allows you to use your implementation normally rather than having to pass it through the interface first.
how does this lib work
This lib basically removes the annoying conversion back to the original type in your implementation and instead does that conversion in a wrapper functions instead. This should now allow you to write more code and less boilerplate.
In essence all this lib does is
...
somefn : *const fn(*anyopaque, u8, []const u8) !?f32
...
to using (VtableFn)
...
somefn : *const fn(struct {*anyopaque, u8, []const u8}) !?f32
...
and your implementation functions from
...
pub fn area(self : Self, times : f32) f32 {
return self.h * self.w * times;
}
...
to using (ToVtableFn)
pub fn areaWrapper(args : struct {*anyopaque, f32}) f32 {
const self : orignal_type = @ptrCast(@alignCast(args[0]));
return @call(.auto, area, {
self,
args[1],
});
}
example
const Iface = @import("zig_interface_testing");
// implemention
const Circle = struct {
const Self = @This();
radius: f32,
pub fn init(r: f32) Self {
return Self{
.radius = r,
};
}
pub fn perimeter(self: Self) f32 {
return std.math.pi * self.radius * 2;
}
pub fn area(self: Self) f32 {
return std.math.pi * self.radius * self.radius;
}
pub fn format(
self: Self,
writer: *std.Io.Writer,
) !void {
try writer.print("Circle : {}", .{self.radius});
}
};
// implemention
const Rect = struct {
const Self = @This();
w: f32,
h: f32,
pub fn init(w: f32, h: f32) Self {
return Self{
.w = w,
.h = h,
};
}
pub fn perimeter(self: Rect) f32 {
return self.h * 2 + self.w * 2;
}
pub fn area(self: Self) f32 {
return self.h * self.w;
}
pub fn format(
self: Self,
writer: *std.Io.Writer,
) !void {
try writer.print("sqaure : {}, {}", .{ self.h, self.w });
}
};
// interface
const Shape = struct {
const Self = @This();
data: *anyopaque,
vtable: *const Vtable,
// create your vtable
const Vtable = struct {
// convert your vtable functions with Iface.VtableFn
perimeter_fn: *const Iface.VtableFn(fn (*anyopaque) f32), // -> *const fn (tuple) returnType
area_fn: *const Iface.VtableFn(fn (*anyopaque) f32),
format_fn: *const Iface.VtableFn(fn (self: *anyopaque, writer: *std.Io.Writer) anyerror!void),
};
pub fn init(shape: anytype) Self {
std.debug.print("shape : {*}\n", .{shape});
const self = Self{
.data = @ptrCast(shape),
.vtable = &Vtable{
// convert your implemation functions to that of the vtable
.perimeter_fn = Iface.ToVtableFn(@field(@TypeOf(shape.*), "perimeter")),
.area_fn = Iface.ToVtableFn(@field(@TypeOf(shape.*), "area")),
.format_fn = Iface.ToVtableFn(@field(@TypeOf(shape.*), "format")),
},
};
return self;
}
pub fn perimeter(self: Self) f32 {
// run them functions
return self.vtable.perimeter_fn(.{self.data});
}
pub fn area(self: Self) f32 {
return self.vtable.area_fn(.{self.data});
}
pub fn format(
self: Self,
writer: *std.Io.Writer,
) !void {
self.vtable.format_fn(.{ self.data, writer }) catch {
return std.Io.Writer.Error.WriteFailed;
};
}
};
Description
Languages
Zig
100%