Search code examples
zig

How to get the field value in comptime in zig


I want to make a deserializer in Zig. Now I could iterate the field information in comptime. But I want to get the field default value in struct. Is there a way to get them? For example, I want to get the zzzz's value 100 in getIntOrDefault.

const std = @import("std");
const meta = std.meta;
const trait = meta.trait;
const assert = std.debug.assert;

pub fn deserializeInto(ptr: anytype) !void {
    const T = @TypeOf(ptr);
    comptime assert(trait.is(.Pointer)(T));

    const C = comptime meta.Child(T);

    ptr.* = switch (C) {
        []const u8 => "test string",
        else => switch (@typeInfo(C)) {
            .Struct => try deserializeStruct(C),
            .Int => try getIntOrDefault(C),
            else => @compileError("Unsupported deserialization type " ++ @typeName(C) ++ "\n"),
        },
    };
}

pub fn getIntOrDefault(comptime T: type) !T {
    return 2;
}

pub fn deserializeStruct(comptime T: type) !T {
    var value: T = undefined;
    inline for (meta.fields(T)) |struct_field| {
        try deserializeInto(&@field(value, struct_field.name));
    }
    return value;
}

pub fn main() !void {
    const T = struct {
        wwww: []const u8,
        zzzz: u32 = 100,
    };

    var v: T = undefined;

    try deserializeInto(&v);
    std.debug.print("zzzz value is {d}\n", .{v.zzzz});
}


Solution

  • Zig 0.11+

    The default value is available as @typeInfo(T).Struct.fields[...].default_value. But it is stored as a pointer to anyopaque, you won't be able to dereference it, unless you cast it with @ptrCast like that:

    const std = @import("std");
    
    pub fn main() !void {
        const TestStruct = struct {
            foo: u32 = 123,
        };
        std.log.info("{}", .{ TestStruct{} });
    
        const foo_field = @typeInfo(TestStruct).Struct.fields[0];
        std.log.info("field name: {s}", .{ foo_field.name });
    
        if (foo_field.default_value) |dvalue| {
            const dvalue_aligned: *const align(foo_field.alignment) anyopaque = @alignCast(dvalue);
            const value = @as(*const foo_field.type, @ptrCast(dvalue_aligned)).*;
            std.log.info("default value: {}", .{ value });
        }
    }
    

    It prints:

    $ zig build run
    info: main.main.TestStruct{ .foo = 123 }
    info: field name: foo
    info: default value: 123
    

    Zig 0.10

    The @ptrCast usage used to look like this:

    const dvalue_aligned = @alignCast(foo_field.alignment, dvalue);
    const value = @ptrCast(*const foo_field.type, dvalue_aligned).*;
    std.log.info("default value: {}", .{ value });