Add generate function for Tuple

Signed-off-by: Francis Bouvier <francis.bouvier@gmail.com>
This commit is contained in:
Francis Bouvier
2023-05-23 15:23:09 +02:00
parent fdafd96003
commit 76c8d8594c
3 changed files with 149 additions and 76 deletions

View File

@@ -6,6 +6,8 @@ fn fmtName(comptime T: type) []const u8 {
return it.first();
}
// Generate a flatten tagged Union from various structs and union of structs
// TODO: make this function more generic
pub const Union = struct {
_enum: type,
_union: type,
@@ -23,7 +25,7 @@ pub const Union = struct {
const tuple_T = @TypeOf(tuple);
const tuple_info = @typeInfo(tuple_T);
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
return error.GenerateUnionArgNotTuple;
return error.GenerateArgNotTuple;
}
const tuple_members = tuple_info.Struct.fields;
@@ -39,7 +41,7 @@ pub const Union = struct {
} else if (member_info == .Struct) {
members_nb += 1;
} else {
return error.GenerateUnionMemberNotUnionOrStruct;
return error.GenerateMemberNotUnionOrStruct;
}
}
@@ -64,7 +66,7 @@ pub const Union = struct {
} else if (members_nb < 65536) {
tag_type = u16;
} else {
return error.GenerateUnionTooMuchMembers;
return error.GenerateTooMuchMembers;
}
// second iteration to generate tags
@@ -100,7 +102,7 @@ pub const Union = struct {
};
const enum_T = @Type(std.builtin.Type{ .Enum = enum_info });
// third iteration to generate union
// third iteration to generate union type
comptime var union_fields: [members_nb]std.builtin.Type.UnionField = undefined;
done = 0;
inline for (tuple_members) |member, i| {
@@ -141,13 +143,123 @@ pub const Union = struct {
}
};
fn itoa(comptime i: u8) ![]u8 {
comptime {
var len: usize = undefined;
if (i < 10) {
len = 1;
} else if (i < 100) {
len = 2;
} else {
return error.GenerateTooMuchMembers;
}
var buf: [len]u8 = undefined;
return try std.fmt.bufPrint(buf[0..], "{d}", .{i});
}
}
// Generate a flatten tuple type from various structs and tuple of structs.
// TODO: make this function more generic
pub fn TupleT(comptime tuple: anytype) type {
// check types provided
const tuple_T = @TypeOf(tuple);
const tuple_info = @typeInfo(tuple_T);
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
@compileError("GenerateArgNotTuple");
}
const tuple_members = tuple_info.Struct.fields;
// first iteration to get the total number of members
comptime var members_nb = 0;
for (tuple_members) |member| {
const member_T = @field(tuple, member.name);
if (@TypeOf(member_T) == type) {
members_nb += 1;
} else {
const member_info = @typeInfo(@TypeOf(member_T));
if (member_info != .Struct and !member_info.Struct.is_tuple) {
@compileError("GenerateMemberNotTypeOrTuple");
}
for (member_info.Struct.fields) |field| {
if (@TypeOf(@field(member_T, field.name)) != type) {
@compileError("GenerateMemberTupleChildNotType");
}
}
members_nb += member_info.Struct.fields.len;
}
}
// second iteration to generate the tuple type
var fields: [members_nb]std.builtin.Type.StructField = undefined;
var done = 0;
while (done < members_nb) {
fields[done] = .{
.name = try itoa(done),
.field_type = type,
.default_value = null,
.is_comptime = false,
.alignment = @alignOf(type),
};
done += 1;
}
const decls: [0]std.builtin.Type.Declaration = undefined;
const info = std.builtin.Type.Struct{
.layout = .Auto,
.fields = &fields,
.decls = &decls,
.is_tuple = true,
};
return @Type(std.builtin.Type{ .Struct = info });
}
// Instantiate a flatten tuple from various structs and tuple of structs
// You need to call first TupleT to generate the according type
// TODO: make this function more generic
pub fn TupleInst(comptime T: type, comptime tuple: anytype) T {
// check types provided
const tuple_T = @TypeOf(tuple);
const tuple_info = @typeInfo(tuple_T);
const tuple_members = tuple_info.Struct.fields;
// instantiate the tuple
var t: T = undefined;
var done = 0;
for (tuple_members) |member| {
const member_T = @field(tuple, member.name);
var member_info: std.builtin.Type = undefined;
if (@TypeOf(member_T) == type) {
member_info = @typeInfo(member_T);
} else {
member_info = @typeInfo(@TypeOf(member_T));
}
var member_detail = member_info.Struct;
if (member_detail.is_tuple) {
for (member_detail.fields) |field| {
const name = try itoa(done);
@field(t, name) = @field(member_T, field.name);
done += 1;
}
} else {
const name = try itoa(done);
@field(t, name) = @field(tuple, member.name);
done += 1;
}
}
return t;
}
// Tests
// -----
const Error = error{
UnionArgNotTuple,
UnionMemberNotUnionOrStruct,
UnionTooMuchMembers,
GenerateArgNotTuple,
GenerateMemberNotUnionOrStruct,
GenerateMemberNotTupleOrStruct,
GenerateMemberTupleNotStruct,
GenerateTooMuchMembers,
};
const Astruct = struct {
@@ -200,4 +312,27 @@ pub fn tests() !void {
try std.testing.expectEqualStrings(from_mix_union.Union.fields[3].name, "Dstruct");
std.debug.print("Generate Union: OK\n", .{});
// Tuple from structs
const tuple_structs = .{ Astruct, Bstruct };
const tFromStructs = TupleInst(TupleT(tuple_structs), tuple_structs);
const t_from_structs = @typeInfo(@TypeOf(tFromStructs));
try std.testing.expect(t_from_structs == .Struct);
try std.testing.expect(t_from_structs.Struct.is_tuple);
try std.testing.expect(t_from_structs.Struct.fields.len == 2);
try std.testing.expect(@field(tFromStructs, "0") == Astruct);
try std.testing.expect(@field(tFromStructs, "1") == Bstruct);
// Tuple from tuple and structs
const tuple_mix = .{ tFromStructs, Cstruct };
const tFromMix = TupleInst(TupleT(tuple_mix), tuple_mix);
const t_from_mix = @typeInfo(@TypeOf(tFromMix));
try std.testing.expect(t_from_mix == .Struct);
try std.testing.expect(t_from_mix.Struct.is_tuple);
try std.testing.expect(t_from_mix.Struct.fields.len == 3);
try std.testing.expect(@field(tFromMix, "0") == Astruct);
try std.testing.expect(@field(tFromMix, "1") == Bstruct);
try std.testing.expect(@field(tFromMix, "2") == Cstruct);
std.debug.print("Generate Tuple: OK\n", .{});
}