Skip to content

Instantly share code, notes, and snippets.

@alexnask
Last active April 22, 2018 12:59
Show Gist options
  • Save alexnask/e299728343571f54fbffcc7f07af78b0 to your computer and use it in GitHub Desktop.
Save alexnask/e299728343571f54fbffcc7f07af78b0 to your computer and use it in GitHub Desktop.
const std = @import("std");
// standard metaprogramming library, exposes wrappers over low level @reflect/@reify stuff.
const meta = std.meta;
pub const VTableEntry = struct {
const Self = this;
name: []const u8,
ftype: type,
fn is_nullable(self: const &Self) bool {
return meta.is_nullable(self.ftype);
}
}
// This type function could be rewritten as:
// return vtable_gen([]VTableEntry {
// VTableEntry { .name = "next", .ftype = fn() ?T, },
// VTableEntry { .name = "reset", .ftype = ?fn() void, },
// });
// With @reify (which would enable us to write vtable_gen)
pub fn IteratorVTable(comptime T: type) type {
return struct {
const Entries = []VTableEntry {
VTableEntry { .name = "next", .ftype = fn()?T, },
VTableEntry { .name = "reset", .ftype = ?fn()void },
};
// These fields would be generated by vtable_gen
nextFn: fn(usize) ?T,
resetFn: ?fn(usize) void,
};
}
fn add_usize_param(comptime FnType: type) type {
@compileError("Need @reflect/@reify.");
}
fn vtable_getter(comptime T: type, comptime name: []const u8, comptime FnType: type) add_usize_param(FnType) {
if (FnType.arg_count > 0) {
@compileError("vtable_getter only supports no-arg functions for now.");
}
return struct {
// TODO: func should have a usize argument + whatever FnType's arguments are.
fn func(s: usize) FnType.ReturnType {
var self = @intToPtr(&T, s);
return @inlineCall(@field(self, name));
}
}.func;
}
fn vtable_from(comptime VTableType: type, comptime ImplType: type) VTableType {
// TODO: This assumes the ImplType is a &struct.
const Unwrapped = meta.child_type(ImplType);
var vtable: VTableType = undefined;
for(VTableType.Entries) |entry| {
if (entry.is_nullable()) {
if (meta.has_method(Unwrapped, entry.name)) {
@field(vtable, entry.name ++ "Fn") = vtable_getter(Unwrapped, entry.name, meta.child_type(entry.ftype));
} else {
@field(vtable, entry.name ++ "Fn") = null;
}
} else {
@field(vtable, entry.name ++ "Fn") = vtable_getter(Unwrapped, entry.name, meta.child_type(entry.ftype));
}
}
return vtable;
}
pub fn Iterator(comptime T: type) type {
return struct {
const Self = this;
self: usize,
vtable_ptr: &const IteratorVTable(T),
pub fn init(iter: var) Self {
return Self {
.self = @ptrToInt(iter),
.vtable_ptr = &comptime vtable_from(IteratorVTable(T), @typeOf(iter)),
}
}
pub fn next(self: &Self) ?T {
return vtable_ptr.nextFn(@ptrToInt(self));
}
pub fn reset(self: &Self) !void {
if (vtable_ptr.resetFn == null) {
return error.NotResetable;
}
(??vtable_ptr.resetFn)(@ptrToInt(self));
}
};
}
// Iterator example
pub fn RepeatIterator(comptime val: var, comptime times: usize) type {
return struct {
const Self = this;
const T = @typeOf(val);
current: usize,
fn init() Self {
return Self {
.current = 0,
};
}
fn next(self: &Self) ?T {
if (self.current < times) {
self.current += 1;
return val;
}
return null;
}
fn reset(self: &Self) void {
self.current = 0;
}
};
}
fn consume_usize_iterator(iter: Iterator(usize)) void {
for (iter.next()) |item| {
std.debug.warn("{}\n", item);
}
}
test "Repeat iterator" {
var iter = RepeatIterator(usize(42), 5).init();
consume_usize_iterator(Iterator(usize).from(iter));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment