init
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.zig-cache/
|
||||
zig-out/
|
||||
158
README.md
Normal file
158
README.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# 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
|
||||
```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;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
56
build.zig
Normal file
56
build.zig
Normal file
@@ -0,0 +1,56 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const mod = b.addModule(
|
||||
"interface",
|
||||
.{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
},
|
||||
);
|
||||
|
||||
// const exe = b.addExecutable(.{
|
||||
// .name = "zig_interface_testing",
|
||||
// .root_module = b.createModule(.{
|
||||
// .root_source_file = b.path("src/main.zig"),
|
||||
// .target = target,
|
||||
// .optimize = optimize,
|
||||
// .imports = &.{
|
||||
// .{ .name = "zig_interface_testing", .module = mod },
|
||||
// },
|
||||
// }),
|
||||
// });
|
||||
|
||||
// b.installArtifact(exe);
|
||||
|
||||
// const run_step = b.step("run", "Run the app");
|
||||
|
||||
// const run_cmd = b.addRunArtifact(exe);
|
||||
// run_step.dependOn(&run_cmd.step);
|
||||
|
||||
// run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
// if (b.args) |args| {
|
||||
// run_cmd.addArgs(args);
|
||||
// }
|
||||
|
||||
// const mod_tests = b.addTest(.{
|
||||
// .root_module = mod,
|
||||
// });
|
||||
|
||||
// const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||
|
||||
// const exe_tests = b.addTest(.{
|
||||
// .root_module = exe.root_module,
|
||||
// });
|
||||
|
||||
// const run_exe_tests = b.addRunArtifact(exe_tests);
|
||||
|
||||
// const test_step = b.step("test", "Run tests");
|
||||
// test_step.dependOn(&run_mod_tests.step);
|
||||
// test_step.dependOn(&run_exe_tests.step);
|
||||
}
|
||||
81
build.zig.zon
Normal file
81
build.zig.zon
Normal file
@@ -0,0 +1,81 @@
|
||||
.{
|
||||
// This is the default name used by packages depending on this one. For
|
||||
// example, when a user runs `zig fetch --save <url>`, this field is used
|
||||
// as the key in the `dependencies` table. Although the user can choose a
|
||||
// different name, most users will stick with this provided value.
|
||||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = .zig_interface_testing,
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
// Together with name, this represents a globally unique package
|
||||
// identifier. This field is generated by the Zig toolchain when the
|
||||
// package is first created, and then *never changes*. This allows
|
||||
// unambiguous detection of one package being an updated version of
|
||||
// another.
|
||||
//
|
||||
// When forking a Zig project, this id should be regenerated (delete the
|
||||
// field and run `zig build`) if the upstream project is still maintained.
|
||||
// Otherwise, the fork is *hostile*, attempting to take control over the
|
||||
// original project's identity. Thus it is recommended to leave the comment
|
||||
// on the following line intact, so that it shows up in code reviews that
|
||||
// modify the field.
|
||||
.fingerprint = 0xea4a93eaf5dec5a2, // Changing this has security and trust implications.
|
||||
// Tracks the earliest Zig version that the package considers to be a
|
||||
// supported use case.
|
||||
.minimum_zig_version = "0.16.0",
|
||||
// This field is optional.
|
||||
// Each dependency must either provide a `url` and `hash`, or a `path`.
|
||||
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
|
||||
// Once all dependencies are fetched, `zig build` no longer requires
|
||||
// internet connectivity.
|
||||
.dependencies = .{
|
||||
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
|
||||
//.example = .{
|
||||
// // When updating this field to a new URL, be sure to delete the corresponding
|
||||
// // `hash`, otherwise you are communicating that you expect to find the old hash at
|
||||
// // the new URL. If the contents of a URL change this will result in a hash mismatch
|
||||
// // which will prevent zig from using it.
|
||||
// .url = "https://example.com/foo.tar.gz",
|
||||
//
|
||||
// // This is computed from the file contents of the directory of files that is
|
||||
// // obtained after fetching `url` and applying the inclusion rules given by
|
||||
// // `paths`.
|
||||
// //
|
||||
// // This field is the source of truth; packages do not come from a `url`; they
|
||||
// // come from a `hash`. `url` is just one of many possible mirrors for how to
|
||||
// // obtain a package matching this `hash`.
|
||||
// //
|
||||
// // Uses the [multihash](https://multiformats.io/multihash/) format.
|
||||
// .hash = "...",
|
||||
//
|
||||
// // When this is provided, the package is found in a directory relative to the
|
||||
// // build root. In this case the package's hash is irrelevant and therefore not
|
||||
// // computed. This field and `url` are mutually exclusive.
|
||||
// .path = "foo",
|
||||
//
|
||||
// // When this is set to `true`, a package is declared to be lazily
|
||||
// // fetched. This makes the dependency only get fetched if it is
|
||||
// // actually used.
|
||||
// .lazy = false,
|
||||
//},
|
||||
},
|
||||
// Specifies the set of files and directories that are included in this package.
|
||||
// Only files and directories listed here are included in the `hash` that
|
||||
// is computed for this package. Only files listed here will remain on disk
|
||||
// when using the zig package manager. As a rule of thumb, one should list
|
||||
// files required for compilation plus any license(s).
|
||||
// Paths are relative to the build root. Use the empty string (`""`) to refer to
|
||||
// the build root itself.
|
||||
// A directory listed here means that all files within, recursively, are included.
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
// For example...
|
||||
//"LICENSE",
|
||||
//"README.md",
|
||||
},
|
||||
}
|
||||
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