Last active
May 7, 2021 12:54
-
-
Save alexnask/932f93596a0fffd600e079fdea7361a5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NOW | |
/// We use this to return multiple fns with different signatures | |
const AppendedSliceGetter = struct { @"fn": anytype }; | |
fn ConstWrappedSlice(comptime Self: type, comptime Elem: type) type { | |
if (!comptime std.meta.trait.is(.Pointer)(Self)) return []const Elem; | |
return if (@typeInfo(Self).Pointer.is_const) []const Elem else []Elem; | |
} | |
/// Given a type that includes an 'appended_slice_info' decl, generate functions | |
/// that will return the zero length array contents as slices | |
fn appendedSliceGetters( | |
comptime Self: type, | |
comptime elem_types: []const type, | |
) []const AppendedSliceGetter { | |
const info = Self.appended_slice_info; | |
var res: [info.len]AppendedSliceGetter = undefined; | |
var i: usize = 0; | |
while (i < info.len) : (i += 1) { | |
assert(info[i].len == 2); | |
res[i] = .{ | |
.@"fn" = struct { | |
fn f(self: anytype) ConstWrappedSlice(@TypeOf(self), info[i][1]) { | |
var self_raw = @ptrToInt(self) + @sizeOf(Self); | |
comptime var j = 0; | |
inline while (j < i) : (j += 1) { | |
self_raw += @sizeOf(info[j][1]) * @field(self, info[j][0]); | |
} | |
return @intToPtr([*]info[i][1], self_raw)[0..@field(self, info[i][0])]; | |
} | |
}.f, | |
}; | |
} | |
return &res; | |
} | |
pub const Example = struct { | |
/// This speicifes that starting at @ptrToInt(self) + @sizeOf(Example) | |
/// we have 'self.names_len' `[]const u8`s and 'self.others_len' `usize`s contiguously in memory | |
const appended_slice_info = .{ | |
.{ "names_len", []const u8 }, | |
.{ "others_len", usize }, | |
}; | |
const getters = appendedSliceGetters(Block); | |
names_len: usize, | |
others_len: usize, | |
/// names is `fn (self: anytype) ConstWrappedSlice(@TypeOf(self), []const u8) | |
/// aka fn (self: *const Example) []const []const u8 | |
/// and fn (self: *Example) [][]const u8 | |
pub const names = getters[0].@"fn"; | |
/// Same as above with usize slices instead of []const u8 slices | |
pub const others = getters[1].@"fn"; | |
}; | |
/// Allocate enough memory for the zero length arrays | |
/// and set the lengths | |
fn allocWithAppendedSlices( | |
allocator: *Allocator, | |
comptime T: type, | |
lens: [T.appended_slice_info.len]usize, | |
) !*T { | |
comptime const info = T.appended_slice_info; | |
var total_size: usize = @sizeOf(T); | |
inline for (info) |curr_info, i| { | |
total_size += lens[i] * @sizeOf(curr_info[1]); | |
} | |
var bytes = try allocator.allocWithOptions( | |
u8, | |
total_size, | |
@alignOf(T), | |
null, | |
); | |
var ret = @ptrCast(*T, bytes.ptr); | |
inline for (info) |curr_info, i| { | |
@field(ret, curr_info[0]) = lens[i]; | |
} | |
return ret; | |
} | |
// With .decls | |
const TypeInfo = std.builtin.TypeInfo; | |
fn AppendedSlicesMixin(comptime Self: type, comptime info: anytype) type { | |
var decls: [info.len + 1]TypeInfo.Declaration = undefined; | |
var slice_struct_fields: [info.len]TypeInfo.StructField = undefined; | |
for (info) |len_info, i| { | |
assert(mem.endsWith(u8, len_info[0], "_len")); | |
const name = len_info[0][0..len_info[0].len - 4]; | |
const elem_type = len_info[1]; | |
// Assuming TypeInfo.Declaration.Data.Var is `anytype` instead of `type` | |
const f = struct { | |
fn f(self: anytype) ConstWrappedSlice(@TypeOf(self), elem_type) { | |
var self_raw = @ptrToInt(self) + @sizeOf(Self); | |
comptime var j = 0; | |
inline while (j < i) : (j += 1) { | |
self_raw += @sizeOf(info[j][1]) * @field(self, info[j][0]); | |
} | |
return @intToPtr([*]elem_type, self_raw)[0..@field(self, len_info[0])]; | |
} | |
}.f; | |
decls[i] = .{ | |
.name = name, | |
.is_pub = true, | |
.data = .{ .Var = f }, | |
}; | |
slice_struct_fields[i] = .{ | |
.name = name, | |
.field_type = []const elem_type, | |
.default_value = @as(?[]const elem_type, null), | |
.is_comptime = false, | |
.alignment = @alignOf([]const elem_type), | |
}; | |
} | |
const slice_pack = @Type(.{ .Struct = .{ | |
.layout = .Auto, | |
.fields = &slice_struct_fields, | |
.decls = &.{}, | |
.is_tuple = false, | |
}}); | |
decls[info.len] = .{ | |
.name = "create", | |
.is_pub = true, | |
.data = .{ | |
.{ .Var = struct { | |
fn alloc(allocator: *Allocator, slices: slice_pack) !*Self { | |
// ... | |
} | |
}.alloc }, | |
}, | |
}; | |
return @Type(.{ .Struct = .{ | |
.layout = .Auto, | |
.fields = &.{}, | |
.decls = &decls, | |
.is_tuple = false, | |
}}); | |
} | |
pub const Example = struct { | |
// Provides `names`, `others` and `create` | |
pub usingnamespace AppendedSlicesMixin(Example, .{ | |
.{ "names_len", []const u8 }, | |
.{ "others_len", usize }, | |
}); | |
names_len: usize, | |
others_len: usize, | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment