Closed4
Zigの@typeInfo()やbuiltinで遊ぶ ( ≒ @compileLogを使ってcomptimeで遊ぶ)
の記事を書いてるときに、@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} })
少し脱線するけど、個人的に便利だなと思った 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でも取得できるので、いろいろ使い道はありそう。
ちなみに @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
と違って一部表示が省略されるので、もっと良い方法がありそう。
@compileLog()
以外のcomptimeのデバッグ方法って何があるんだろうと思ったけれど、下記議論を見ても特に方法はなさそう。加えてテストを書くというのは確かに重要。
このスクラップは2023/06/11にクローズされました