const std = @import("std"); fn A(comptime T: type) type { const ti = @typeInfo(T).@"struct"; var names: [ti.fields.len + ti.decls.len][]const u8 = undefined; var types: [ti.fields.len + ti.decls.len]type = undefined; var attrs: [ti.fields.len + ti.decls.len]std.builtin.Type.StructField.Attributes = undefined; inline for (ti.fields, 0..) |field, idx| { names[idx] = field.name; types[idx] = extern struct { value: field.type, pub fn get(self: @This()) field.type { std.debug.print("getting {s}\n", .{field.name}); return self.value; } pub fn set(self: @This(), value: field.type) void { std.debug.print("setting {s}\n", .{field.name}); self.value = value; } }; const default = if (field.default_value_ptr) |def| types[idx] {.value = @as(*field.type, @constCast(@ptrCast(@alignCast(def)))).*} else null; attrs[idx] = .{ .@"comptime" = field.is_comptime, .@"align" = field.alignment, .default_value_ptr = &default, }; } inline for (ti.decls, 0..) |decl, idx| { const default = struct { pub fn call() void { std.debug.print("calling {s}\n", .{decl.name}); @call(.auto, @field(T, decl.name), .{}); std.debug.print("done calling {s}\n", .{decl.name}); } }; names[ti.fields.len + idx] = decl.name; types[ti.fields.len + idx] = type; attrs[ti.fields.len + idx] = .{.default_value_ptr = &default}; } return @Struct(.auto, null, &names, &types, &attrs); } pub fn main() !void { const a = A(struct { i: i32 = 123, j: i32 = 456, pub fn b() void { std.debug.print("B!\n", .{}); } pub fn c() void { std.debug.print("C!\n", .{}); } }) {}; std.debug.print("[{}] {}\n", .{@TypeOf(a), a}); a.b.call(); a.c.call(); std.debug.print("{}\n", .{a.i.get()}); std.debug.print("{}\n", .{a.j.get()}); }