Skip to content

Instantly share code, notes, and snippets.

@alexnask
Last active May 7, 2021 12:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexnask/932f93596a0fffd600e079fdea7361a5 to your computer and use it in GitHub Desktop.
Save alexnask/932f93596a0fffd600e079fdea7361a5 to your computer and use it in GitHub Desktop.
// 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