zigでinterface的なやつ
fieldParentPtrイディオムは極めてシンプルな実装になっている。しかしfieldParentPtrイディオムはLLVMの最適化と相性が悪いらしい。zig ver0.9からはAllocgateと呼ばれる手法でstd.mem.Allocatorやstd.rand.Randomが多相化されている。*anyopaque
を使って型を無視することで動的に実装を選択できるようになっている。ええんかそれで。標準ライブラリでそれやってるくらいならそのうちに言語機能にtraitなりinterfaceなり実装されそうな気がするが、これの構文糖みたいなもんになるのだろうか。
const std = @import("std"); /// Use @fieldParentPtr /// https://zig.news/david_vanderson/interfaces-in-zig-o1c const FooI = struct { fooFn: fn(*FooI) void, fn foo(self: *FooI) void { self.fooFn(self); } }; const Bar = struct { sym: u8, fooI: FooI, fn init() Bar { return .{ .sym = 0xff, .fooI = FooI { .fooFn = fooFn } }; } fn fooFn(fooI: *FooI) void { const self = @fieldParentPtr(Bar, "fooI", fooI); std.debug.print("foo({}) from Bar\n", .{self.sym}); } }; /// Allocgate /// https://pithlessly.github.io/allocgate.html /// https://github.com/ziglang/zig/blob/master/lib/std/mem/Allocator.zig /// https://github.com/ziglang/zig/blob/master/lib/std/rand.zig const HogeI = struct { ptr: *anyopaque, hogeFn: fn (ptr: *anyopaque) void, fn init(pointer: anytype, comptime hogeFn: fn (ptr: @TypeOf(pointer)) void) HogeI { const Ptr = @TypeOf(pointer); std.debug.assert(@typeInfo(Ptr) == .Pointer); // Must be a pointer std.debug.assert(@typeInfo(Ptr).Pointer.size == .One); // Must be a single-item pointer std.debug.assert(@typeInfo(@typeInfo(Ptr).Pointer.child) == .Struct); // Must point to a struct const gen = struct { fn hoge(ptr: *anyopaque) void { const alignment = @typeInfo(Ptr).Pointer.alignment; const self = @ptrCast(Ptr, @alignCast(alignment, ptr)); @call(.{.modifier = .always_inline}, hogeFn, .{self}); } }; return .{ .ptr = pointer, .hogeFn = gen.hoge }; } fn hoge(self: HogeI) void { self.hogeFn(self.ptr); } }; const Fuga = struct { sym: u8, fn init() Fuga { return .{ .sym = 0xff / 2 }; } fn hogeI(self: *Fuga) HogeI { return HogeI.init(self, hogeFn); } fn hogeFn(self: *Fuga) void { std.debug.print("hoo({}) from Fuga\n", .{self.sym}); } }; pub fn main() !void { var bar = Bar.init(); bar.fooI.foo(); var fuga = Fuga.init(); var hogeI = fuga.hogeI(); hogeI.hoge(); }