Getting the name of a struct field @runtime

I’ll try and explain what I need to get working.
In the base code I work with the Terms array.

Now I am making a function which has to extract the name of one of the arrays, when passing the address of one ScorePair argument using the global var terms.

pub fn using_scorepair(sp: *const ScorePair) void {
    // in which array resides this sp argument (kind of doable if the compiled order is the same)
    // what is the declaration name of that array inside Terms (without if if if if)
}

pub const terms: *const Terms = &default_terms; // default_terms not included here.

pub const Terms = struct {
    piece_value: [6]ScorePair,
    king_passed_pawn_distance: [8]ScorePair,
    enemy_king_passed_pawn_distance: [8]ScorePair,
    pawn_phalanx: [8]ScorePair,
    passed_pawn: [8]ScorePair,
    protected_pawn: [8]ScorePair,
    doubled_pawn: [8]ScorePair,
    isolated_pawn: [8]ScorePair,
    backward_pawn: [8]ScorePair,
    king_cannot_reach_passed_pawn: ScorePair,
    bishop_pair: ScorePair,
    tempo: ScorePair,
    knight_mobility: [9]ScorePair,
    bishop_mobility: [14]ScorePair,
    rook_mobility: [15]ScorePair,
    queen_mobility: [28]ScorePair,
    attack_power: [6][8]ScorePair,
    // some more....
};

pub const ScorePair = struct {
    mg: i16,
    eg: i16,
};

Probably can’t be done without a pointer to Terms, but if that’s fine, I came up with this comptime monster:

pub const Terms = struct {
    piece_value: [6]ScorePair,
    king_passed_pawn_distance: [8]ScorePair,
    enemy_king_passed_pawn_distance: [8]ScorePair,
    pawn_phalanx: [8]ScorePair,
    passed_pawn: [8]ScorePair,
    protected_pawn: [8]ScorePair,
    doubled_pawn: [8]ScorePair,
    isolated_pawn: [8]ScorePair,
    backward_pawn: [8]ScorePair,
    king_cannot_reach_passed_pawn: ScorePair,
    bishop_pair: ScorePair,
    tempo: ScorePair,
    knight_mobility: [9]ScorePair,
    bishop_mobility: [14]ScorePair,
    rook_mobility: [15]ScorePair,
    queen_mobility: [28]ScorePair,
    attack_power: [6][8]ScorePair,
    // some more....
};

pub const ScorePair = struct {
    mg: i16,
    eg: i16,
};

fn isScorePairType(T: type) bool {
    if (T == ScorePair)
        return true;

    return switch (@typeInfo(T)) {
        .array => |array| isScorePairType(array.child),
        else => false,
    };
}

fn scorePairName(terms: *const Terms, pair: *const ScorePair) []const u8 {
    const names = comptime blk: {
        var names: [@sizeOf(Terms) / @sizeOf(ScorePair)][]const u8 = @splat("UNKNOWN");
        for (std.meta.fields(Terms)) |field| {
            if (!isScorePairType(field.type))
                @compileError("Field '" ++ field.name ++ "' is not a (array of) ScorePair");

            const start_idx = @offsetOf(Terms, field.name) / @sizeOf(ScorePair);
            const end_idx = start_idx + @sizeOf(field.type) / @sizeOf(ScorePair);

            names[start_idx..end_idx].* = @splat(field.name);
        }
        break :blk names;
    };

    const terms_arr: []const ScorePair = @ptrCast(terms);
    std.debug.assert(@intFromPtr(pair) > @intFromPtr(terms_arr.ptr));
    const idx = pair - terms_arr.ptr;
    std.debug.assert(idx < terms_arr.len);
    return names[idx];
}

pub fn main() void {
    const some_terms: Terms = undefined;
    std.debug.print("{s}\n", .{scorePairName(&some_terms, &some_terms.isolated_pawn[3])});
}

const std = @import("std");
1 Like

That looks like a working comptime monster. I don’t care if it is monster :slight_smile:
Thx. I’ll try and go from this one.

1 Like