However, it is impossible to add methods to generated types, they must be inert bundles of fields.
I’ve never tried doing this with methods, but it seems like I can gather arbitrary sets of fields on a type. I recently did this in my new windowing project zin:
pub fn Callback(window_config: WindowConfig) type {
return makeTaggedUnion(
&([_]Field{
.{ .name = "close", .type = void },
.{ .name = "draw", .type = Draw(window_config) },
} ++ (if (window_config.data().key_events) [_]Field{
.{ .name = "key", .type = Key },
} else [_]Field{}) ++ (if (window_config.data().mouse_events) [_]Field{
.{ .name = "mouse", .type = Mouse },
} else [_]Field{}) ++ (if (window_config.data().timers) [_]Field{
.{ .name = "timer", .type = usize },
} else [_]Field{})),
);
}
This code generates a tagged union that looks like this:
const Callback = union(enum) {
// only include this if app wants mouse events
mouse: Mouse,
// only include this if app wants key events
key: Key,
// etc...
};
Couldn’t I use a similar technique to create a type and filter on decls?
P.S. here’s the makeTaggedUnion
function:
const Field = struct {
name: [:0]const u8,
type: type,
};
fn makeTaggedUnion(fields: []const Field) type {
const EnumField = std.builtin.Type.EnumField;
const UnionField = std.builtin.Type.UnionField;
var enum_fields: [fields.len]EnumField = undefined;
var union_fields: [fields.len]UnionField = undefined;
for (fields, 0..) |field, i| {
enum_fields[i] = .{ .name = field.name, .value = i };
union_fields[i] = .{ .name = field.name, .type = field.type, .alignment = @alignOf(field.type) };
}
return @Type(std.builtin.Type{
.@"union" = .{
.layout = .auto,
.tag_type = @Type(std.builtin.Type{ .@"enum" = .{
.tag_type = std.math.IntFittingRange(0, fields.len - 1),
.fields = &enum_fields,
.decls = &.{},
.is_exhaustive = true,
} }),
.fields = &union_fields,
.decls = &.{},
},
});
}