Closed4

Zigの@typeInfo()やbuiltinで遊ぶ ( ≒ @compileLogを使ってcomptimeで遊ぶ)

funatsufumiyafunatsufumiya

https://zenn.dev/funatsufumiya/articles/1e2d4da94ba151

の記事を書いてるときに、@typeInfoの中身を文字として表示するには、printだとcomptimeの制約があって全てを表示できるわけではないようで、どうするのがベターかなと考えてたんだけど、@compileLog() を使うのがシンプルでよさそう。これならstdもいらない。

main.zig
//const std = @import("std");

const Vec = struct {
    x: u32,
    y: u32,
    z: u32,
};

pub fn main() !void {
    const v = Vec{ .x = 1, .y = 2, .z = 3, };
    const _type = @TypeOf(v);
    const _typeInfo = @typeInfo(_type);
    // std.debug.print("{any}\n", .{v});
    // std.debug.print("{any}\n", .{_type});
    @compileLog(_typeInfo);
}

// Compile Log Output:
// @as(builtin.Type, .{ .Struct = .{.layout = .Auto, .backing_integer = null, .fields = .{ .{ ... }, .{ ... }, .{ ... } }, .decls = .{  }, .is_tuple = false} })
funatsufumiyafunatsufumiya

少し脱線するけど、個人的に便利だなと思った builtin の機能は、OSやCPUの情報などを取得できること。

Compile-Variables | Zig Language Reference

const std = @import("std");
const builtin = @import("builtin");

pub fn main() !void {
    std.debug.print("os: {any}\n", .{builtin.os});
    std.debug.print("cpu: {any}\n", .{builtin.cpu});
}

// os: target.Target.Os{ .tag = target.Target.Os.Tag.macos, .version_range = target.Target.Os.VersionRange@16bb061d0 }
// cpu: target.Target.Cpu{ .arch = target.Target.Cpu.Arch.aarch64, .model = target.Target.Cpu.Model{ .name = { 97, 112, 112, 108, 101, 95, 97, 49, 52 }, .llvm_name = { 97, 112, 112, 108, 101, 45, 97, 49, 52 }, .features = target.Target.Cpu.Feature.Set{ .ints = { ... } } }, .features = target.Target.Cpu.Feature.Set{ .ints = { 18194784854749493184, 14619274031135, 9297546225161077696, 44, 0 } } }

前述の@compileLog()で確認すると、実行時だけでなくcomptimeでも取得できるので、いろいろ使い道はありそう。

funatsufumiyafunatsufumiya

ちなみに @compileLog() を使って、@typeInfo()の結果をさらに掘り下げていくこともできて、なんだかRubyっぽい。言語内の型がちゃんと構造体としてbuiltin.Typeに定義されているのが、メタくて楽しい。

main.zig
// const std = @import("std");

const Vec = struct {
    x: u32,
    y: u32,
    z: u32,
};

pub fn main() !void {
    const v = Vec{ .x = 1, .y = 2, .z = 3, };
    const _type = @TypeOf(v);
    const _typeInfo = @typeInfo(_type);
    const _type2 = @TypeOf(_typeInfo);
    const _typeInfo2 = @typeInfo(_type2);
    // std.debug.print("{any}\n", .{v});
    // std.debug.print("{any}\n", .{_type});
    // @compileLog(_typeInfo);
    @compileLog(_typeInfo2, _typeInfo2.Union.fields, _typeInfo2.Union.decls);
}

// Compile Log Output:
// @as(builtin.Type, .{ .Union = .{.layout = .Auto, .tag_type = @typeInfo(builtin.Type).Union.tag_type.?, .fields = .{ .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... } }, .decls = .{ .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... }, .{ ... } }} })
// , @as([]const builtin.Type.UnionField, .{ .{.name = "Type\x00", .type = void, .alignment = 0}, .{.name = "Void\x00", .type = void, .alignment = 0}, .{.name = "Bool\x00", .type = void, .alignment = 0}, .{.name = "NoReturn\x00", .type = void, .alignment = 0}, .{.name = "Int\x00", .type = builtin.Type.Int, .alignment = 2}, .{.name = "Float\x00", .type = builtin.Type.Float, .alignment = 2}, .{.name = "Pointer\x00", .type = builtin.Type.Pointer, .alignment = 8}, .{.name = "Array\x00", .type = builtin.Type.Array, .alignment = 8}, .{.name = "Struct\x00", .type = builtin.Type.Struct, .alignment = 8}, .{.name = "ComptimeFloat\x00", .type = void, .alignment = 0}, .{.name = "ComptimeInt\x00", .type = void, .alignment = 0}, .{.name = "Undefined\x00", .type = void, .alignment = 0}, .{.name = "Null\x00", .type = void, .alignment = 0}, .{.name = "Optional\x00", .type = builtin.Type.Optional, .alignment = 0}, .{.name = "ErrorUnion\x00", .type = builtin.Type.ErrorUnion, .alignment = 0}, .{.name = "ErrorSet\x00", .type = ?[]const builtin.Type.Error, .alignment = 8}, .{.name = "Enum\x00", .type = builtin.Type.Enum, .alignment = 8}, .{.name = "Union\x00", .type = builtin.Type.Union, .alignment = 8}, .{.name = "Fn\x00", .type = builtin.Type.Fn, .alignment = 1}, .{.name = "Opaque\x00", .type = builtin.Type.Opaque, .alignment = 8}, .{.name = "Frame\x00", .type = builtin.Type.Frame, .alignment = 8}, .{.name = "AnyFrame\x00", .type = builtin.Type.AnyFrame, .alignment = 0}, .{.name = "Vector\x00", .type = builtin.Type.Vector, .alignment = 0}, .{.name = "EnumLiteral\x00", .type = void, .alignment = 0} })
// , @as([]const builtin.Type.Declaration, .{ .{.name = "Int", .is_pub = true}, .{.name = "Float", .is_pub = true}, .{.name = "Pointer", .is_pub = true}, .{.name = "Array", .is_pub = true}, .{.name = "ContainerLayout", .is_pub = true}, .{.name = "StructField", .is_pub = true}, .{.name = "Struct", .is_pub = true}, .{.name = "Optional", .is_pub = true}, .{.name = "ErrorUnion", .is_pub = true}, .{.name = "Error", .is_pub = true}, .{.name = "ErrorSet", .is_pub = true}, .{.name = "EnumField", .is_pub = true}, .{.name = "Enum", .is_pub = true}, .{.name = "UnionField", .is_pub = true}, .{.name = "Union", .is_pub = true}, .{.name = "FnArg", .is_pub = true}, .{.name = "Fn", .is_pub = true}, .{.name = "Opaque", .is_pub = true}, .{.name = "Frame", .is_pub = true}, .{.name = "AnyFrame", .is_pub = true}, .{.name = "Vector", .is_pub = true}, .{.name = "Declaration", .is_pub = true} })

ただ、debug.printと違って一部表示が省略されるので、もっと良い方法がありそう。

このスクラップは2023/06/11にクローズされました