# 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. # using this lib ```bash $ zig fetch --save git+https://git.sirlilpanda.studio/sirlilpanda/zig-interface-helpers ``` in `build.zig` ```zig const iface = b.dependency("interface", .{ .target = target, .optimize = optimize, }); exe.root_module.addImport("interface", iface.module("interface")); ``` # 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 ```zig ... somefn : *const fn(*anyopaque, u8, []const u8) !?f32 ... ``` to using (`VtableFn`) ```zig ... somefn : *const fn(struct {*anyopaque, u8, []const u8}) !?f32 ... ``` and your implementation functions from ```zig ... pub fn area(self : Self, times : f32) f32 { return self.h * self.w * times; } ... ``` to using (`ToVtableFn`) ```zig pub fn areaWrapper(args : struct {*anyopaque, f32}) f32 { const self : orignal_type = @ptrCast(@alignCast(args[0])); return @call(.auto, area, { self, args[1], }); } ``` # example ```zig 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; }; } }; ```