Okay, @biosbob, this actually works for the *const F
- I do this by distinguishing between reification and initialization here and I do that through another indirection. In short, I don’t see a way to do it through direct reification, but if you can stomach assignments via an inline for
then this should do it for you:
const std = @import("std");
const print = std.log.debug;
const Impl = struct {
pub fn foo(x: u32) void {
print("foo({d})", .{x});
}
pub fn bar() void {
print("bar", .{});
}
};
fn ItabType(T: type) type {
comptime {
const TI = @typeInfo(T);
const decls = TI.Struct.decls;
var fld_list: [decls.len]std.builtin.Type.StructField = undefined;
for (decls, 0..) |decl, i| {
const func = @field(T, decl.name);
fld_list[i] = std.builtin.Type.StructField{
.name = decl.name,
.type = *const @TypeOf(func),
.default_value = null,
.is_comptime = false,
.alignment = 0,
};
}
const freeze = fld_list;
return @Type(.{ .Struct = .{
.layout = .auto,
.fields = freeze[0..],
.decls = &.{},
.is_tuple = false,
.backing_integer = null,
}});
}
}
pub fn initItab(comptime impl: type) ItabType(impl) {
var itab: ItabType(impl) = undefined;
inline for (comptime std.meta.declarations(impl)) |decl| {
@field(itab, decl.name) = @field(impl, decl.name);
}
return itab;
}
pub fn main() void {
const itab = initItab(Impl);
itab.bar();
}
What’s even funnier here is that this also works using a comptime keyword on the init function itself:
pub fn main() void {
const itab = comptime initItab(Impl);
itab.bar();
}
So… maybe you can actually get both constraints satisfied at once here? Odd. I think someone should look a little deeper into the discrepancy here.