{"id":20332479,"url":"https://github.com/permutationlock/zimpl","last_synced_at":"2025-04-11T21:32:58.735Z","repository":{"id":207085544,"uuid":"718113091","full_name":"permutationlock/zimpl","owner":"permutationlock","description":"Simple comptime generic interfaces for Zig","archived":false,"fork":false,"pushed_at":"2025-03-22T21:35:02.000Z","size":206,"stargazers_count":35,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-22T22:31:36.403Z","etag":null,"topics":["generics","interfaces","polymorphism","traits","vtables","zig"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/permutationlock.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.MIT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-13T12:08:52.000Z","updated_at":"2025-03-22T21:35:05.000Z","dependencies_parsed_at":"2023-12-14T03:42:41.026Z","dependency_job_id":"d2c8b05b-db31-464d-bc9c-0226558efe6e","html_url":"https://github.com/permutationlock/zimpl","commit_stats":{"total_commits":64,"total_committers":1,"mean_commits":64.0,"dds":0.0,"last_synced_commit":"8b98b8587846037b5a1037c303603c0d55b6f349"},"previous_names":["permutationlock/zimpl"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/permutationlock%2Fzimpl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/permutationlock%2Fzimpl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/permutationlock%2Fzimpl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/permutationlock%2Fzimpl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/permutationlock","download_url":"https://codeload.github.com/permutationlock/zimpl/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248482928,"owners_count":21111402,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["generics","interfaces","polymorphism","traits","vtables","zig"],"created_at":"2024-11-14T20:26:50.192Z","updated_at":"2025-04-11T21:32:58.724Z","avatar_url":"https://github.com/permutationlock.png","language":"Zig","readme":"# Zimpl Zig interfaces\n\nA dead simple implementation of [static dispatch][2] interfaces in Zig\nthat emerged from a tiny subset of [ztrait][1]. See [here][3]\nfor some motivation.\n\nAlso included is a compatible implementation of [dynamic dispatch][4]\ninterfaces via `comptime` generated [vtables][5]. Inspired by\n[`interface.zig`][6].\n\n*Warning: Zimpl is still mostly an exploratory project.*\n\n## Static dispatch\n\n### `Impl`\n\n```Zig\npub fn Impl(comptime Ifc: fn (type) type, comptime T: type) type { ... }\n```\n\n### Definitions\n\nIf `T` is a single-item pointer type, then define `U(T)` to be the child type,\ni.e. `T = *U(T)`, otherwise define `U(T)=T`.\n\n### Arguments\n\nThe function `Ifc` must always return a struct type.\nIf `U(T)` has a declaration matching the name of a field from\n`Ifc(T)` that cannot coerce to the type of that field, then a\ncompile error will occur (and a pretty good one now, thank you Zig\nCore Team).\n\n### Return value\n\nThe type `Impl(Ifc, T)` is a struct type with the same fields\nas `Ifc(T)`, but with the default value of each field set equal to\nthe declaration of `U(T)` of the same name, if such a declaration\nexists.\n\n### Example\n\n```Zig\n// An interface\npub fn Reader(comptime T: type) type {\n    return struct {\n        ReadError: type = anyerror,\n        read: fn (reader_ctx: T, buffer: []u8) anyerror!usize,\n    };\n}\n\n// A collection of functions using the interface\npub const io = struct {\n    pub inline fn read(\n        reader_ctx: anytype,\n        reader_impl: Impl(Reader, @TypeOf(reader_ctx)),\n        buffer: []u8,\n    ) reader_impl.ReadError!usize {\n        return @errorCast(reader_impl.read(reader_ctx, buffer));\n    }\n\n    pub inline fn readAll(\n        reader_ctx: anytype,\n        reader_impl: Impl(Reader, @TypeOf(reader_ctx)),\n        buffer: []u8,\n    ) reader_impl.ReadError!usize {\n        return readAtLeast(reader_ctx, reader_impl, buffer, buffer.len);\n    }\n\n    pub inline fn readAtLeast(\n        reader_ctx: anytype,\n        reader_impl: Impl(Reader, @TypeOf(reader_ctx)),\n        buffer: []u8,\n        len: usize,\n    ) reader_impl.ReadError!usize {\n        assert(len \u003c= buffer.len);\n        var index: usize = 0;\n        while (index \u003c len) {\n            const amt = try read(reader_ctx, reader_impl, buffer[index..]);\n            if (amt == 0) break;\n            index += amt;\n        }\n        return index;\n    }\n};\n\ntest \"define and use a reader\" {\n    const FixedBufferReader = struct {\n        buffer: []const u8,\n        pos: usize = 0,\n\n        pub const ReadError = error{};\n\n        pub fn read(self: *@This(), out_buffer: []u8) ReadError!usize {\n            const len = @min(self.buffer[self.pos..].len, out_buffer.len);\n            @memcpy(out_buffer[0..len], self.buffer[self.pos..][0..len]);\n            self.pos += len;\n            return len;\n        }\n    };\n    const in_buf: []const u8 = \"I really hope that this works!\";\n    var reader = FixedBufferReader{ .buffer = in_buf };\n\n    var out_buf: [16]u8 = undefined;\n    const len = try io.readAll(\u0026reader, .{}, \u0026out_buf);\n\n    try testing.expectEqualStrings(in_buf[0..len], out_buf[0..len]);\n}\n\ntest \"use std.fs.File as a reader\" {\n    var buffer: [19]u8 = undefined;\n    var file = try std.fs.cwd().openFile(\"my_file.txt\", .{});\n    try io.readAll(file, .{}, \u0026buffer);\n\n    try std.testing.expectEqualStrings(\"Hello, I am a file!\", \u0026buffer);\n}\n\ntest \"use std.os.fd_t as a reader via an explicitly defined interface\" {\n    var buffer: [19]u8 = undefined;\n    const fd = try std.os.open(\"my_file.txt\", std.os.O.RDONLY, 0);\n    try io.readAll(\n        fd,\n        .{ .read = std.os.read, .ReadError = std.os.ReadError, },\n        \u0026buffer,\n    );\n\n    try std.testing.expectEqualStrings(\"Hello, I am a file!\", \u0026buffer);\n}\n```\n\n## Dynamic dispatch\n\n### `VIfc`\n\n```Zig\npub fn VIfc(comptime Ifc: fn (type) type) type { ... }\n```\n### Arguments\n\nThe `Ifc` function must always return a struct type.\n\n### Return value\n\nReturns a struct of the following form:\n```Zig\nstruct {\n    ctx: *anyopaque,\n    vtable: VTable(Ifc),\n\n    pub fn init(\n        comptime access: CtxAccess,\n        ctx: anytype,\n        impl: Impl(Ifc, CtxType(@TypeOf(ctx), access)),\n    ) @This() {\n        return .{\n            .ctx = if (access == .indirect) @constCast(ctx) else ctx,\n            .vtable = vtable(Ifc, access, @TypeOf(ctx), impl),\n        };\n    }\n};\n```\nThe struct type `VTable(Ifc)` contains one field for each field of\n`Ifc(*anyopaque)` that is a (optional) function. The type\nof each vtable field is converted to a (optional) function pointer\nwith the same signature.\n\nThe `init` function constructs a virtual interface from a given\nruntime context and interface implementation. Since the\ncontext is stored as a type-erased pointer, the `access` parameter is provided\nto allow vtables to be constructed for implementations that rely on\nnon-pointer contexts.\n\n```Zig\npub const CtxAccess = enum { direct, indirect };\n\nfn CtxType(comptime Ctx: type, comptime access: CtxAccess) type {\n    return if (access == .indirect) @typeInfo(Ctx).Pointer.child else Ctx;\n}\n```\n\nIf `access` is `.direct`, then the type-erased `ctx` pointer stored\nin `VIfc(Ifc)` is cast as the correct pointer type and passed directly to\nconcrete member function implementations.\n\nOtherwise, if `access` is `.indirect`, `ctx` is a pointer to the actual\ncontext, and it is dereferenced and passed by value to member\nfunctions.\n\n### Example\n\n```Zig\n// An interface\npub fn Reader(comptime T: type) type {\n    return struct {\n        // non-function fields are fine, but vtable interfaces ignore them\n        ReadError: type = anyerror,\n        read: fn (reader_ctx: T, buffer: []u8) anyerror!usize,\n    };\n}\n\n// A collection of functions using virtual 'Reader' interfaces\npub const vio = struct {\n    pub inline fn read(reader: VIfc(Reader), buffer: []u8) anyerror!usize {\n        return reader.vtable.read(reader.ctx, buffer);\n    }\n\n    pub inline fn readAll(reader: VIfc(Reader), buffer: []u8) anyerror!usize {\n        return readAtLeast(reader, buffer, buffer.len);\n    }\n\n    pub fn readAtLeast(\n        reader: VIfc(Reader),\n        buffer: []u8,\n        len: usize,\n    ) anyerror!usize {\n        assert(len \u003c= buffer.len);\n        var index: usize = 0;\n        while (index \u003c len) {\n            const amt = try read(reader, buffer[index..]);\n            if (amt == 0) break;\n            index += amt;\n        }\n        return index;\n    }\n};\n\ntest \"define and use a reader\" {\n    const FixedBufferReader = struct {\n        buffer: []const u8,\n        pos: usize = 0,\n\n        pub const ReadError = error{};\n\n        pub fn read(self: *@This(), out_buffer: []u8) ReadError!usize {\n            const len = @min(self.buffer[self.pos..].len, out_buffer.len);\n            @memcpy(out_buffer[0..len], self.buffer[self.pos..][0..len]);\n            self.pos += len;\n            return len;\n        }\n    };\n    const in_buf: []const u8 = \"I really hope that this works!\";\n    var reader = FixedBufferReader{ .buffer = in_buf };\n\n    var out_buf: [16]u8 = undefined;\n    const len = try vio.readAll(Reader.init(.direct, \u0026reader, .{}), \u0026out_buf);\n\n    try testing.expectEqualStrings(in_buf[0..len], out_buf[0..len]);\n}\n\ntest \"use std.fs.File as a reader\" {\n    var buffer: [19]u8 = undefined;\n    var file = try std.fs.cwd().openFile(\"my_file.txt\", .{});\n    try vio.readAll(Reader.init(.indirect, \u0026file, .{}), \u0026buffer);\n\n    try std.testing.expectEqualStrings(\"Hello, I am a file!\", \u0026buffer);\n}\n\ntest \"use std.os.fd_t as a reader via an explicitly defined interface\" {\n    var buffer: [19]u8 = undefined;\n    const fd = try std.os.open(\"my_file.txt\", std.os.O.RDONLY, 0);\n    try vio.readAll(\n        Reader.init(\n            .indirect,\n            \u0026fd,\n            .{ .read = std.os.read, .ReadError = std.os.ReadError },\n        ),\n        \u0026buffer,\n    );\n\n    try std.testing.expectEqualStrings(\"Hello, I am a file!\", \u0026buffer);\n}\n```\n\n[1]: https://github.com/permutationlock/ztrait\n[2]: https://en.wikipedia.org/wiki/Static_dispatch\n[3]: https://github.com/permutationlock/zimpl/blob/main/why.md\n[4]: https://en.wikipedia.org/wiki/Dynamic_dispatch\n[5]: https://en.wikipedia.org/wiki/Virtual_method_table\n[6]: https://github.com/alexnask/interface.zig\n","funding_links":[],"categories":["Zig"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpermutationlock%2Fzimpl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpermutationlock%2Fzimpl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpermutationlock%2Fzimpl/lists"}