Skip to content

Commit

Permalink
Updated makeVIfc to be more ergonomic.
Browse files Browse the repository at this point in the history
  • Loading branch information
permutationlock committed Dec 31, 2023
1 parent a251df4 commit 75316e9
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 71 deletions.
43 changes: 29 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ with the same signature.
```Zig
pub fn makeVIfc(
comptime Ifc: fn (type) type,
comptime access: CtxAccess,
) fn (ctx: anytype, impl: Impl(Ifc, CtxType(Ctx, access))) VIfc(Ifc) { ... }
) fn (CtxAccess, anytype, anytype) VIfc(Ifc) { ... }
```

### Arguments
Expand All @@ -166,13 +165,29 @@ The `Ifc` function must always return a struct type.
### Return value

Returns a function to construct a `VIfc(Ifc)` vtable interface from a
concrete runtime context and corresponding interface implementation.
concrete runtime context and corresponding interface implementation
with the following signature.

```Zig
fn makeFn(
comptime access: CtxAccess,
ctx: anytype,
impl: Impl(Ifc, CtxType(@TypeOf(ctx), access)),
) VIfc(Ifc)
```

Since vtable interfaces store their
context as a type-erased pointer, the `access` parameter is provided
to allow vtables to be constructed for implementations that rely on
non-pointer contexts.

```Zig
pub const CtxAccess = enum { Direct, Indirect };
```

Since vtable interfaces store
contexts as type-erased pointers, the `access` parameters allows for
vtables to work with non-pointer contexts by adding a layer
if indirection: the pointer to the context is dereferenced and passed
by value to the concrete interface function implementations.
If `access` is `.Indirect`, then the conetxt pointer
is dereferenced and passed
by value to member function implementations.

### Example

Expand All @@ -186,9 +201,8 @@ pub fn Reader(comptime T: type) type {
};
}
// Functions to construct virtual 'Reader' interface implementations
const makeReader = makeVIfc(Reader, .Direct);
const makeReaderI = makeVIfc(Reader, .Indirect);
// Function to construct a virtual 'Reader' interface implementation
const makeReader = makeVIfc(Reader);
// A collection of functions using virtual 'Reader' interfaces
pub const vio = struct {
Expand Down Expand Up @@ -234,15 +248,15 @@ test "define and use a reader" {
var reader = FixedBufferReader{ .buffer = in_buf };
var out_buf: [16]u8 = undefined;
const len = try vio.readAll(makeReader(&reader, .{}), &out_buf);
const len = try vio.readAll(makeReader(.Direct, &reader, .{}), &out_buf);
try testing.expectEqualStrings(in_buf[0..len], out_buf[0..len]);
}
test "use std.fs.File as a reader" {
var buffer: [19]u8 = undefined;
var file = try std.fs.cwd().openFile("my_file.txt", .{});
try vio.readAll(makeReaderI(&file, .{}), &buffer);
try vio.readAll(makeReader(.Indirect, &file, .{}), &buffer);
try std.testing.expectEqualStrings("Hello, I am a file!", &buffer);
}
Expand All @@ -251,7 +265,8 @@ test "use std.os.fd_t as a reader via an explicitly defined interface" {
var buffer: [19]u8 = undefined;
const fd = try std.os.open("my_file.txt", std.os.O.RDONLY, 0);
try vio.readAll(
makeReaderI(
makeReader(
.Indirect,
&fd,
.{
.read = std.os.read,
Expand Down
1 change: 1 addition & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub fn build(b: *Builder) !void {
.{ .name = "io", .path = "examples/io.zig" },
.{ .name = "read_file", .path = "examples/read_file.zig" },
.{ .name = "vcount", .path = "examples/vcount.zig" },
.{ .name = "vcount2", .path = "examples/vcount2.zig" },
};

const test_step = b.step("test", &.{});
Expand Down
2 changes: 1 addition & 1 deletion examples/io.zig
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ pub inline fn isBytes(
if (!std.mem.eql(u8, slice[i..][0..len], buffer[0..len])) {
matches = false;
}
try skipBytes(reader_ctx, reader_impl, len);
try skipBytes(reader_ctx, reader_impl, len, .{});
i += len;
} else {
if (slice[i] != try readByte(reader_ctx, reader_impl)) {
Expand Down
2 changes: 1 addition & 1 deletion examples/io/FixedBufferReader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ test "read and seek" {
test "virtual read" {
const buffer: []const u8 = "I really hope that this works!";
var stream = @This(){ .buffer = buffer, .pos = 0 };
const reader = vio.makeReader(&stream, .{});
const reader = vio.makeReader(.Direct, &stream, .{});

var out_buf: [buffer.len]u8 = undefined;
const len1 = try vio.readAll(reader, &out_buf);
Expand Down
4 changes: 2 additions & 2 deletions examples/io/FixedBufferStream.zig
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ test "virtual write and read back" {

var stream_buf: [in_buf.len]u8 = undefined;
var stream = @This(){ .buffer = &stream_buf, .pos = 0 };
const writer = vio.makeWriter(&stream, .{});
const writer = vio.makeWriter(.Direct, &stream, .{});

try vio.writeAll(writer, in_buf);

stream.pos = 0;

const reader = vio.makeReader(&stream, .{});
const reader = vio.makeReader(.Direct, &stream, .{});
var out_buf: [in_buf.len]u8 = undefined;
const rlen = try vio.readAll(reader, &out_buf);

Expand Down
2 changes: 1 addition & 1 deletion examples/io/buffered_reader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ test "virtual buffered fixed buffer reader" {
const buffer = "Hello! Is anybody there?";
var fb_reader = io.FixedBufferReader{ .buffer = buffer };
var buff_reader = bufferedReader(8, &fb_reader, .{});
const reader = vio.makeReader(&buff_reader, .{});
const reader = vio.makeReader(.Direct, &buff_reader, .{});
try std.testing.expect(vio.isBufferedReader(reader));

var out_bytes: [buffer.len]u8 = undefined;
Expand Down
2 changes: 1 addition & 1 deletion examples/io/buffered_writer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ test "count bytes written to null_writer" {
test "virtual count bytes written to null_writer" {
var count_writer = io.countingWriter(io.null_writer, .{});
var buff_writer = bufferedWriter(8, &count_writer, .{});
const writer = vio.makeWriter(&buff_writer, .{});
const writer = vio.makeWriter(.Direct, &buff_writer, .{});
try vio.writeAll(writer, "Hello!");
try std.testing.expectEqual(@as(usize, 0), count_writer.bytes_written);
try vio.writeAll(writer, "Is anybody there?");
Expand Down
8 changes: 4 additions & 4 deletions examples/read_file.zig
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ test "virtual read file with std.fs.File" {
var buffer: [32]u8 = undefined;
var fbs: FixedBufferStream = .{ .buffer = &buffer };
try vio.streamUntilDelimiter(
vio.makeReaderI(&file, .{}),
vio.makeWriter(&fbs, .{}),
vio.makeReader(.Indirect, &file, .{}),
vio.makeWriter(.Direct, &fbs, .{}),
'\n',
32,
);
Expand All @@ -90,11 +90,11 @@ test "virtual read file with std.os.fd_t" {
var buffer: [32]u8 = undefined;
var fbs: FixedBufferStream = .{ .buffer = &buffer };
try vio.streamUntilDelimiter(
vio.makeReaderI(&fd, .{
vio.makeReader(.Indirect, &fd, .{
.read = std.os.read,
.ReadError = std.os.ReadError,
}),
vio.makeWriter(&fbs, .{}),
vio.makeWriter(.Direct, &fbs, .{}),
'\n',
32,
);
Expand Down
6 changes: 4 additions & 2 deletions examples/vcount.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const VIfc = zimpl.VIfc;
const makeVIfc = zimpl.makeVIfc;

const Counter = VIfc(@import("count.zig").Counter);
const makeCounter = makeVIfc(@import("count.zig").Counter, .Direct);
const makeCounter = makeVIfc(@import("count.zig").Counter);

pub fn countToTen(ctr: Counter) void {
while (ctr.vtable.read(ctr.ctx) < 10) {
Expand All @@ -25,6 +25,7 @@ test "explicit implementation" {
};
var count: usize = 0;
countToTen(makeCounter(
.Direct,
&count,
.{ .increment = USize.inc, .read = USize.deref },
));
Expand All @@ -45,7 +46,7 @@ const MyCounter = struct {

test "infer implementation" {
var my_counter: MyCounter = .{ .count = 0 };
countToTen(makeCounter(&my_counter, .{}));
countToTen(makeCounter(.Direct, &my_counter, .{}));
try testing.expectEqual(@as(usize, 10), my_counter.count);
}

Expand All @@ -56,6 +57,7 @@ fn otherInc(self: *MyCounter) void {
test "override implementation" {
var my_counter: MyCounter = .{ .count = 0 };
countToTen(makeCounter(
.Direct,
&my_counter,
.{ .increment = otherInc },
));
Expand Down
6 changes: 2 additions & 4 deletions examples/vio.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ const makeVIfc = zimpl.makeVIfc;
const io = @import("io.zig");

pub const Reader = VIfc(io.Reader);
pub const makeReader = makeVIfc(io.Reader, .Direct);
pub const makeReaderI = makeVIfc(io.Reader, .Indirect);
pub const makeReader = makeVIfc(io.Reader);

pub inline fn read(reader: Reader, buffer: []u8) anyerror!usize {
return reader.vtable.read(reader.ctx, buffer);
Expand Down Expand Up @@ -247,8 +246,7 @@ pub fn readEnum(
}

pub const Writer = VIfc(io.Writer);
pub const makeWriter = makeVIfc(io.Writer, .Direct);
pub const makeWriterI = makeVIfc(io.Writer, .Indirect);
pub const makeWriter = makeVIfc(io.Writer);

pub inline fn write(writer: Writer, bytes: []const u8) anyerror!usize {
return writer.vtable.write(writer.ctx, bytes);
Expand Down
Loading

0 comments on commit 75316e9

Please sign in to comment.