init
This commit is contained in:
231
src/main.zig
Normal file
231
src/main.zig
Normal file
@@ -0,0 +1,231 @@
|
||||
const std = @import("std");
|
||||
const Io = std.Io;
|
||||
const Iface = @import("zig_interface_testing");
|
||||
|
||||
const HelperIMPL = struct {
|
||||
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});
|
||||
}
|
||||
};
|
||||
|
||||
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 });
|
||||
}
|
||||
};
|
||||
|
||||
const Shape = struct {
|
||||
const Self = @This();
|
||||
data: *anyopaque,
|
||||
vtable: *const Vtable,
|
||||
|
||||
const Vtable = struct {
|
||||
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{
|
||||
.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 {
|
||||
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;
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const NormalIMPL = struct {
|
||||
const Circle = struct {
|
||||
const Self = @This();
|
||||
radius: f32,
|
||||
|
||||
pub fn init(r: f32) Self {
|
||||
return Self{
|
||||
.radius = r,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn perimeter(self: *anyopaque) f32 {
|
||||
const cric: *Self = @ptrCast(@alignCast(self));
|
||||
return std.math.pi * cric.radius * 2;
|
||||
}
|
||||
|
||||
pub fn area(self: *anyopaque) f32 {
|
||||
const cric: *Self = @ptrCast(@alignCast(self));
|
||||
return std.math.pi * cric.radius * cric.radius;
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
self: *anyopaque,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
const cric: *Self = @ptrCast(@alignCast(self));
|
||||
try writer.print("Circle : {}", .{cric.radius});
|
||||
}
|
||||
};
|
||||
|
||||
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: *anyopaque) f32 {
|
||||
const rect: *Self = @ptrCast(@alignCast(self));
|
||||
return rect.h * 2 + rect.w * 2;
|
||||
}
|
||||
|
||||
pub fn area(self: *anyopaque) f32 {
|
||||
const rect: *Self = @ptrCast(@alignCast(self));
|
||||
|
||||
return rect.h * rect.w;
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
self: *anyopaque,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
const rect: *Self = @ptrCast(@alignCast(self));
|
||||
try writer.print("sqaure : {}, {}", .{ rect.h, rect.w });
|
||||
}
|
||||
};
|
||||
|
||||
const Shape = struct {
|
||||
const Self = @This();
|
||||
data: *anyopaque,
|
||||
vtable: *const Vtable,
|
||||
|
||||
const Vtable = struct {
|
||||
perimeter_fn: *const fn (*anyopaque) f32, // -> *const fn (tuple) returnType
|
||||
area_fn: *const fn (*anyopaque) f32,
|
||||
format_fn: *const 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{
|
||||
.perimeter_fn = @field(@TypeOf(shape.*), "perimeter"),
|
||||
.area_fn = @field(@TypeOf(shape.*), "area"),
|
||||
.format_fn = @field(@TypeOf(shape.*), "format"),
|
||||
},
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn perimeter(self: Self) f32 {
|
||||
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;
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
_ = init;
|
||||
var rect = HelperIMPL.Rect{
|
||||
.h = 5,
|
||||
.w = 10,
|
||||
};
|
||||
|
||||
var circle = HelperIMPL.Circle{
|
||||
.radius = 10,
|
||||
};
|
||||
|
||||
var rect_shape = HelperIMPL.Shape.init(&rect);
|
||||
var circle_shape = HelperIMPL.Shape.init(&circle);
|
||||
|
||||
std.debug.print("rect : area {}, prim : {}, {any} {*}.\n", .{ rect.area(), rect.perimeter(), rect, &rect });
|
||||
std.debug.print("rect_shape : area {}, prim : {}, {f}.\n", .{ rect_shape.area(), rect_shape.perimeter(), rect_shape });
|
||||
|
||||
std.debug.print("circle : area {}, prim : {}, {any} {*}.\n", .{ circle.area(), circle.perimeter(), circle, &circle });
|
||||
std.debug.print("circle_shape : area {}, prim : {}, {f}.\n", .{ circle_shape.area(), circle_shape.perimeter(), circle_shape });
|
||||
}
|
||||
149
src/root.zig
Normal file
149
src/root.zig
Normal file
@@ -0,0 +1,149 @@
|
||||
//! This a is a meta library that implements 2 functions:
|
||||
//! - VtableFn which simply converts a given function like `fn(arg1: u32, arg2: u8) f32` -> `fn(args : struct{u32, u8}) f32`
|
||||
//! - ToVtableFn which wraps the passed function to be the form of VtableFn
|
||||
//! using these you can implement interface with less boilerplate
|
||||
//!
|
||||
//!
|
||||
//! an example can be seen below:
|
||||
//! ```zig
|
||||
//!
|
||||
//! const A = struct {
|
||||
//! f: u8,
|
||||
//!
|
||||
//! pub fn z(a: A) u8 {
|
||||
//! return a + 2;
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! const B = struct {
|
||||
//! f: u8,
|
||||
//!
|
||||
//! pub fn z(b: B) u8 {
|
||||
//! return b + 3;
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! const C = struct {
|
||||
//! data: *anyopaque,
|
||||
//! vtable: *const VTable,
|
||||
//!
|
||||
//! const VTable = struct {
|
||||
//! z_fn: *const VtableFn(fn (*anyopaque) u8),
|
||||
//! };
|
||||
//!
|
||||
//! pub fn init(letter: anytype) C {
|
||||
//! return C{
|
||||
//! .data = @ptrCast(letter),
|
||||
//! .vtable = VTable{
|
||||
//! .z_fn = ToVtableFn(@field(@TypeOf(letter.*), "z")),
|
||||
//! },
|
||||
//! };
|
||||
//! }
|
||||
//!
|
||||
//! pub fn z(c: C) u8 {
|
||||
//! return c.vtable.z_fn(.{c.data});
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//!
|
||||
//! fn usage() void {
|
||||
//! var a = A{
|
||||
//! .f = 2;
|
||||
//! }
|
||||
//!
|
||||
//! var b = B{
|
||||
//! .f = 2;
|
||||
//! }
|
||||
//! // yes this has to be a pointer
|
||||
//! var c : C = C.init(&a);
|
||||
//! _ = c.z();
|
||||
//! }
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! both structs A & B implementing a function z
|
||||
//! C is an interface for A & B that has some pointer to the data (data) and a vtable
|
||||
//! to hold the function pointers. Within the vtable you call VtableFn on the function
|
||||
//! signature you plan to implement.
|
||||
//! within the init function, first the given pointer to the letter is cast to the anyopaque
|
||||
//! yes this value must be passed in to the function as a pointer if you dont want to deal with @constcast
|
||||
//! next the `z` function is gotten from the namespace of the letter `@field(@TypeOf(letter.*), "z")`
|
||||
//! this gotten `z` function is now passed to `ToVtableFn` which returns the wrapped function.
|
||||
//!
|
||||
//! finally to call this function use the signature `{interface}.vtable.{function_name}(.{function args})`
|
||||
//! where the all args for the function must be passed through as a tuple.
|
||||
//!
|
||||
//!
|
||||
//! how `@field(@TypeOf(letter.*), "z")` works:
|
||||
//! - `letter.*` gets the plain type not the pointer to it
|
||||
//! - `@TypeOf(_)` get the type that the defernced letter is
|
||||
//! - `@field(_. "z")` finally gets the function (no the pointer to the function the function itself)
|
||||
//!
|
||||
const std = @import("std");
|
||||
|
||||
/// all this function
|
||||
pub fn VtableFn(function: anytype) type {
|
||||
return @Fn(
|
||||
&.{std.meta.ArgsTuple(function)},
|
||||
&.{.{}},
|
||||
@typeInfo(function).@"fn".return_type orelse void,
|
||||
.{
|
||||
.@"callconv" = @typeInfo(function).@"fn".calling_convention,
|
||||
.varargs = @typeInfo(function).@"fn".is_var_args,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn VtableFnArgs(function: anytype) []const type {
|
||||
var new_tuple_type: []const type = &.{
|
||||
*anyopaque,
|
||||
};
|
||||
|
||||
// @compileLog(@TypeOf(function));
|
||||
|
||||
inline for (@typeInfo(std.meta.ArgsTuple(function)).@"struct".fields, 0..) |field, i| {
|
||||
if (i == 0) continue; // skip the first one
|
||||
new_tuple_type = new_tuple_type ++ .{field.type};
|
||||
}
|
||||
|
||||
return new_tuple_type;
|
||||
}
|
||||
|
||||
fn ToVtableFnType(function: anytype) type {
|
||||
// still have to swap all args to *anyopaque
|
||||
|
||||
return @Fn(
|
||||
&.{@Tuple(VtableFnArgs(function))},
|
||||
&.{.{}},
|
||||
@typeInfo(function).@"fn".return_type orelse void,
|
||||
.{
|
||||
.@"callconv" = @typeInfo(function).@"fn".calling_convention,
|
||||
.varargs = @typeInfo(function).@"fn".is_var_args,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// wraps a given function to be in the form of `fn(tuple) return type` where the tuple will always have the first field being of type `*anyopaque`
|
||||
/// this wrapper internally then converts the type back to that as in the orignal function and calls it
|
||||
pub fn ToVtableFn(comptime function: anytype) fn (@Tuple(VtableFnArgs(@TypeOf(function)))) (@typeInfo(@TypeOf(function)).@"fn".return_type orelse void) {
|
||||
// @compileLog(@Tuple(VtableFnArgs(@TypeOf(function))));
|
||||
|
||||
return struct { // | these args are the ones that replace the @this with *anyopaque
|
||||
pub fn functionWrapper(args: @Tuple(VtableFnArgs(@TypeOf(function)))) (@typeInfo(@TypeOf(function)).@"fn".return_type orelse void) {
|
||||
const orignal_type = @typeInfo(@TypeOf(function)).@"fn".params[0].type orelse @compileError("function need at least one type");
|
||||
|
||||
var type_casted_args: std.meta.ArgsTuple(@TypeOf(function)) = undefined;
|
||||
|
||||
// yeah i have to do this as args isnt a pointer according to zig (even though it is)
|
||||
type_casted_args[0] = @as(**orignal_type, @ptrCast(@alignCast(@constCast(&args[0])))).*.*;
|
||||
|
||||
inline for (args, 0..) |arg, i| {
|
||||
if (i == 0) continue; // skip the first one
|
||||
type_casted_args[i] = arg;
|
||||
}
|
||||
|
||||
// | these args are the ones where its converted back to the original type
|
||||
return @call(.auto, function, type_casted_args);
|
||||
}
|
||||
}.functionWrapper;
|
||||
}
|
||||
Reference in New Issue
Block a user