const std = @import("std"); fn Args(params: []const std.builtin.Type.Fn.Param) type { var argTypes: [params.len]type = undefined; for (params, 0..) |p, i| { argTypes[i] = p.type.?; } return @Tuple(&argTypes); } fn Val(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 fi = @typeInfo(@TypeOf(@field(T, decl.name))).@"fn"; const NT = struct { pub fn call(self: @This(), args: Args(fi.params)) (fi.return_type orelse void) { _ = self; std.debug.print("calling {s}\n", .{decl.name}); @call(.auto, @field(T, decl.name), args); 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 = Val(struct { i: i32 = 123, j: i32 = 456, s: Val(struct { i: i32 = 789, }) = .{}, pub fn b() void { std.debug.print("B!\n", .{}); } pub fn c(self: @This()) void { std.debug.print("C! ({})\n", .{self}); } pub fn d(self: @This(), i: i32) void { self.i += i; std.debug.print("D! ({})\n", .{self}); } }){}; std.debug.print("[{}] {}\n", .{ @TypeOf(a), a }); a.b.call(.{}); a.c.call(.{a}); a.c.call(.{a, 5}); std.debug.print("{}\n", .{a.i.get()}); a.j.set(9); std.debug.print("{}\n", .{a.j.get()}); }