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] = 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, @ptrCast(@alignCast(@constCast(def)))).* } else null; attrs[idx] = .{ .@"comptime" = field.is_comptime, .@"align" = field.alignment, .default_value_ptr = &default, }; } inline for (ti.decls, 0..) |decl, idx| { names[ti.fields.len + idx] = decl.name; const NT = struct { pub fn call(self: @This()) void { _ = self; std.debug.print("calling {s}\n", .{decl.name}); @call(.auto, @field(T, decl.name), .{}); std.debug.print("done calling {s}\n", .{decl.name}); } }; types[ti.fields.len + idx] = NT; const default = NT {}; attrs[ti.fields.len + idx] = .{ .default_value_ptr = &default }; } return @Struct(.auto, null, &names, &types, &attrs); } pub fn main() !void { var a = A(struct { i: i32 = 123, j: i32 = 456, s: A(struct { i: i32 = 789, }) = .{}, 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()}); a.j.set(9); std.debug.print("{}\n", .{a.j.get()}); }