Reflecting over top-level containers imported by other top-level containers

based on answers from other posts, here’s a snip of some code i use when reflecting over a top-level container (file scope):

inline for (@typeInfo(T).Struct.decls) |decl| {
    const fld = @field(T, decl.name);
    const FT = @TypeOf(fld);
    const ti = @typeInfo(FT);
        // do stuff
}

using ti, i can determine whether fld is a struct; and using @hasDecl(FT, "feature"), i can learn more about my duck-typed struct type

finally, i can execute fld.feature to use this part of the struct value

my question/confusion occurs when my “struct” is actually the @import of another zig file… in the earlier snip, fld itself is already a type; i don’t seem to need the @TypeOf(fld) to extract @typeInfo… but what isn’t clear, is how do i actually interact with the (singleton??) value of this file-level container struct ???

conceptually, a top-level import like const Mod = @import("./Mod.zig") seems to blur the line between a struct type and its (singleton) value… selecting S.foo is not the same as selecting s.foo from some instance of S

and yet, when i write Mod.foo i actually retrieve the value of the foo decl – suggesting that Mod in ths context is really a value

(another dimension to my confusion is that we naturally use “decls” within a file-level container, compared with “fields” in a struct type… i have discovered you can actually declare “fields” at file scope…)

so what am i really trying to do???

  • i want to reflect on a file-level container, visiting all of its decls
  • i want to find all such decls bound to some @import("./Mod.zig")
  • knowing these decls are struct’s, i then want to query whether they contain some other public decl of a well-known name; and finally
  • i want to retrieve the value of the decl found inside the imported file

to simplify the problem, these decls within the imported file are pub const

how do i do this???

@This() returns the innermost container (struct, enum, or union) type.
At top level file scope:

const T = @This();
comptime {
    inline for (@typeInfo(T).Struct.decls) |decl| {
        const decl_value = @field(T, decl.name);
    }
}

@import returns the struct type corresponding to the file.

const T = @import("./Mod.zig");
comptime {
    inline for (@typeInfo(T).Struct.decls) |decl| {
        const decl_value = @field(T, decl.name);
    }
}

decl.name is the name of the nested declaration, compare it to the well-known name.


It is not clear to me; if you ask for the file container type: it is T; if you ask for the nested declarations in file: they are decl_value.

const T = @import("./Mod.zig");
comptime {
    inline for (@typeInfo(T).Struct.decls) |decl| {
        const decl_value = @field(T, decl.name);
    }
}
2 Likes

all is well with your solution (which is basically what i was doing)…

the problem was that i received an ambiguous reference error on my equivalent of your @field(T, decl.name) and presumed it was my confusion about this mode of reflection (which i’m actually doing recursively to construct of partial-ordering of my program’s @import graph…

turns out that a “leaf” module was merging multiple namespaces of legacy C #defines that themselves had the same name declared multiple times…

while i had early used this module without issue, the “total inspection” of all of its declarations through my recursive reflective function found this problem…

because zig is so “lazy” about compiling stuff i don’t appear to use explicitly, it makes me a little lazy as well :wink:

1 Like