mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 21:08:36 +00:00
modernize std.meta
This commit is contained in:
parent
fb6b94f80f
commit
7396b144ba
114
lib/std/meta.zig
114
lib/std/meta.zig
@ -7,13 +7,12 @@ const testing = std.testing;
|
||||
|
||||
pub const trait = @import("meta/trait.zig");
|
||||
|
||||
const TypeId = builtin.TypeId;
|
||||
const TypeInfo = builtin.TypeInfo;
|
||||
|
||||
pub fn tagName(v: var) []const u8 {
|
||||
const T = @TypeOf(v);
|
||||
switch (@typeInfo(T)) {
|
||||
TypeId.ErrorSet => return @errorName(v),
|
||||
.ErrorSet => return @errorName(v),
|
||||
else => return @tagName(v),
|
||||
}
|
||||
}
|
||||
@ -55,7 +54,7 @@ test "std.meta.tagName" {
|
||||
|
||||
pub fn stringToEnum(comptime T: type, str: []const u8) ?T {
|
||||
inline for (@typeInfo(T).Enum.fields) |enumField| {
|
||||
if (std.mem.eql(u8, str, enumField.name)) {
|
||||
if (mem.eql(u8, str, enumField.name)) {
|
||||
return @field(T, enumField.name);
|
||||
}
|
||||
}
|
||||
@ -74,9 +73,9 @@ test "std.meta.stringToEnum" {
|
||||
|
||||
pub fn bitCount(comptime T: type) comptime_int {
|
||||
return switch (@typeInfo(T)) {
|
||||
TypeId.Bool => 1,
|
||||
TypeId.Int => |info| info.bits,
|
||||
TypeId.Float => |info| info.bits,
|
||||
.Bool => 1,
|
||||
.Int => |info| info.bits,
|
||||
.Float => |info| info.bits,
|
||||
else => @compileError("Expected bool, int or float type, found '" ++ @typeName(T) ++ "'"),
|
||||
};
|
||||
}
|
||||
@ -88,7 +87,7 @@ test "std.meta.bitCount" {
|
||||
|
||||
pub fn alignment(comptime T: type) comptime_int {
|
||||
//@alignOf works on non-pointer types
|
||||
const P = if (comptime trait.is(TypeId.Pointer)(T)) T else *T;
|
||||
const P = if (comptime trait.is(.Pointer)(T)) T else *T;
|
||||
return @typeInfo(P).Pointer.alignment;
|
||||
}
|
||||
|
||||
@ -102,9 +101,9 @@ test "std.meta.alignment" {
|
||||
|
||||
pub fn Child(comptime T: type) type {
|
||||
return switch (@typeInfo(T)) {
|
||||
TypeId.Array => |info| info.child,
|
||||
TypeId.Pointer => |info| info.child,
|
||||
TypeId.Optional => |info| info.child,
|
||||
.Array => |info| info.child,
|
||||
.Pointer => |info| info.child,
|
||||
.Optional => |info| info.child,
|
||||
else => @compileError("Expected pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"),
|
||||
};
|
||||
}
|
||||
@ -118,9 +117,9 @@ test "std.meta.Child" {
|
||||
|
||||
pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {
|
||||
return switch (@typeInfo(T)) {
|
||||
TypeId.Struct => |info| info.layout,
|
||||
TypeId.Enum => |info| info.layout,
|
||||
TypeId.Union => |info| info.layout,
|
||||
.Struct => |info| info.layout,
|
||||
.Enum => |info| info.layout,
|
||||
.Union => |info| info.layout,
|
||||
else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"),
|
||||
};
|
||||
}
|
||||
@ -148,22 +147,22 @@ test "std.meta.containerLayout" {
|
||||
a: u8,
|
||||
};
|
||||
|
||||
testing.expect(containerLayout(E1) == TypeInfo.ContainerLayout.Auto);
|
||||
testing.expect(containerLayout(E2) == TypeInfo.ContainerLayout.Packed);
|
||||
testing.expect(containerLayout(E3) == TypeInfo.ContainerLayout.Extern);
|
||||
testing.expect(containerLayout(S1) == TypeInfo.ContainerLayout.Auto);
|
||||
testing.expect(containerLayout(S2) == TypeInfo.ContainerLayout.Packed);
|
||||
testing.expect(containerLayout(S3) == TypeInfo.ContainerLayout.Extern);
|
||||
testing.expect(containerLayout(U1) == TypeInfo.ContainerLayout.Auto);
|
||||
testing.expect(containerLayout(U2) == TypeInfo.ContainerLayout.Packed);
|
||||
testing.expect(containerLayout(U3) == TypeInfo.ContainerLayout.Extern);
|
||||
testing.expect(containerLayout(E1) == .Auto);
|
||||
testing.expect(containerLayout(E2) == .Packed);
|
||||
testing.expect(containerLayout(E3) == .Extern);
|
||||
testing.expect(containerLayout(S1) == .Auto);
|
||||
testing.expect(containerLayout(S2) == .Packed);
|
||||
testing.expect(containerLayout(S3) == .Extern);
|
||||
testing.expect(containerLayout(U1) == .Auto);
|
||||
testing.expect(containerLayout(U2) == .Packed);
|
||||
testing.expect(containerLayout(U3) == .Extern);
|
||||
}
|
||||
|
||||
pub fn declarations(comptime T: type) []TypeInfo.Declaration {
|
||||
return switch (@typeInfo(T)) {
|
||||
TypeId.Struct => |info| info.decls,
|
||||
TypeId.Enum => |info| info.decls,
|
||||
TypeId.Union => |info| info.decls,
|
||||
.Struct => |info| info.decls,
|
||||
.Enum => |info| info.decls,
|
||||
.Union => |info| info.decls,
|
||||
else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"),
|
||||
};
|
||||
}
|
||||
@ -232,17 +231,17 @@ test "std.meta.declarationInfo" {
|
||||
}
|
||||
|
||||
pub fn fields(comptime T: type) switch (@typeInfo(T)) {
|
||||
TypeId.Struct => []TypeInfo.StructField,
|
||||
TypeId.Union => []TypeInfo.UnionField,
|
||||
TypeId.ErrorSet => []TypeInfo.Error,
|
||||
TypeId.Enum => []TypeInfo.EnumField,
|
||||
.Struct => []TypeInfo.StructField,
|
||||
.Union => []TypeInfo.UnionField,
|
||||
.ErrorSet => []TypeInfo.Error,
|
||||
.Enum => []TypeInfo.EnumField,
|
||||
else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
|
||||
} {
|
||||
return switch (@typeInfo(T)) {
|
||||
TypeId.Struct => |info| info.fields,
|
||||
TypeId.Union => |info| info.fields,
|
||||
TypeId.Enum => |info| info.fields,
|
||||
TypeId.ErrorSet => |errors| errors.?, // must be non global error set
|
||||
.Struct => |info| info.fields,
|
||||
.Union => |info| info.fields,
|
||||
.Enum => |info| info.fields,
|
||||
.ErrorSet => |errors| errors.?, // must be non global error set
|
||||
else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
|
||||
};
|
||||
}
|
||||
@ -277,10 +276,10 @@ test "std.meta.fields" {
|
||||
}
|
||||
|
||||
pub fn fieldInfo(comptime T: type, comptime field_name: []const u8) switch (@typeInfo(T)) {
|
||||
TypeId.Struct => TypeInfo.StructField,
|
||||
TypeId.Union => TypeInfo.UnionField,
|
||||
TypeId.ErrorSet => TypeInfo.Error,
|
||||
TypeId.Enum => TypeInfo.EnumField,
|
||||
.Struct => TypeInfo.StructField,
|
||||
.Union => TypeInfo.UnionField,
|
||||
.ErrorSet => TypeInfo.Error,
|
||||
.Enum => TypeInfo.EnumField,
|
||||
else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
|
||||
} {
|
||||
inline for (comptime fields(T)) |field| {
|
||||
@ -318,8 +317,8 @@ test "std.meta.fieldInfo" {
|
||||
|
||||
pub fn TagType(comptime T: type) type {
|
||||
return switch (@typeInfo(T)) {
|
||||
TypeId.Enum => |info| info.tag_type,
|
||||
TypeId.Union => |info| if (info.tag_type) |Tag| Tag else null,
|
||||
.Enum => |info| info.tag_type,
|
||||
.Union => |info| if (info.tag_type) |Tag| Tag else null,
|
||||
else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"),
|
||||
};
|
||||
}
|
||||
@ -365,7 +364,7 @@ test "std.meta.activeTag" {
|
||||
///Given a tagged union type, and an enum, return the type of the union
|
||||
/// field corresponding to the enum tag.
|
||||
pub fn TagPayloadType(comptime U: type, tag: @TagType(U)) type {
|
||||
testing.expect(trait.is(builtin.TypeId.Union)(U));
|
||||
testing.expect(trait.is(.Union)(U));
|
||||
|
||||
const info = @typeInfo(U).Union;
|
||||
|
||||
@ -387,30 +386,26 @@ test "std.meta.TagPayloadType" {
|
||||
testing.expect(MovedEvent == @TypeOf(e.Moved));
|
||||
}
|
||||
|
||||
///Compares two of any type for equality. Containers are compared on a field-by-field basis,
|
||||
/// Compares two of any type for equality. Containers are compared on a field-by-field basis,
|
||||
/// where possible. Pointers are not followed.
|
||||
pub fn eql(a: var, b: @TypeOf(a)) bool {
|
||||
const T = @TypeOf(a);
|
||||
|
||||
switch (@typeId(T)) {
|
||||
builtin.TypeId.Struct => {
|
||||
const info = @typeInfo(T).Struct;
|
||||
|
||||
switch (@typeInfo(T)) {
|
||||
.Struct => |info| {
|
||||
inline for (info.fields) |field_info| {
|
||||
if (!eql(@field(a, field_info.name), @field(b, field_info.name))) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
builtin.TypeId.ErrorUnion => {
|
||||
.ErrorUnion => {
|
||||
if (a) |a_p| {
|
||||
if (b) |b_p| return eql(a_p, b_p) else |_| return false;
|
||||
} else |a_e| {
|
||||
if (b) |_| return false else |b_e| return a_e == b_e;
|
||||
}
|
||||
},
|
||||
builtin.TypeId.Union => {
|
||||
const info = @typeInfo(T).Union;
|
||||
|
||||
.Union => |info| {
|
||||
if (info.tag_type) |_| {
|
||||
const tag_a = activeTag(a);
|
||||
const tag_b = activeTag(b);
|
||||
@ -427,31 +422,26 @@ pub fn eql(a: var, b: @TypeOf(a)) bool {
|
||||
|
||||
@compileError("cannot compare untagged union type " ++ @typeName(T));
|
||||
},
|
||||
builtin.TypeId.Array => {
|
||||
.Array => {
|
||||
if (a.len != b.len) return false;
|
||||
for (a) |e, i|
|
||||
if (!eql(e, b[i])) return false;
|
||||
return true;
|
||||
},
|
||||
builtin.TypeId.Vector => {
|
||||
const info = @typeInfo(T).Vector;
|
||||
.Vector => |info| {
|
||||
var i: usize = 0;
|
||||
while (i < info.len) : (i += 1) {
|
||||
if (!eql(a[i], b[i])) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
builtin.TypeId.Pointer => {
|
||||
const info = @typeInfo(T).Pointer;
|
||||
switch (info.size) {
|
||||
builtin.TypeInfo.Pointer.Size.One,
|
||||
builtin.TypeInfo.Pointer.Size.Many,
|
||||
builtin.TypeInfo.Pointer.Size.C,
|
||||
=> return a == b,
|
||||
builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len,
|
||||
}
|
||||
.Pointer => |info| {
|
||||
return switch (info.size) {
|
||||
.One, .Many, .C, => a == b,
|
||||
.Slice => a.ptr == b.ptr and a.len == b.len,
|
||||
};
|
||||
},
|
||||
builtin.TypeId.Optional => {
|
||||
.Optional => {
|
||||
if (a == null and b == null) return true;
|
||||
if (a == null or b == null) return false;
|
||||
return eql(a.?, b.?);
|
||||
|
||||
@ -7,17 +7,11 @@ const warn = debug.warn;
|
||||
|
||||
const meta = @import("../meta.zig");
|
||||
|
||||
//This is necessary if we want to return generic functions directly because of how the
|
||||
// the type erasure works. see: #1375
|
||||
fn traitFnWorkaround(comptime T: type) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub const TraitFn = @TypeOf(traitFnWorkaround);
|
||||
pub const TraitFn = fn (type) bool;
|
||||
|
||||
//////Trait generators
|
||||
|
||||
//Need TraitList because compiler can't do varargs at comptime yet
|
||||
// TODO convert to tuples when #4335 is done
|
||||
pub const TraitList = []const TraitFn;
|
||||
pub fn multiTrait(comptime traits: TraitList) TraitFn {
|
||||
const Closure = struct {
|
||||
@ -60,8 +54,7 @@ pub fn hasFn(comptime name: []const u8) TraitFn {
|
||||
if (!comptime isContainer(T)) return false;
|
||||
if (!comptime @hasDecl(T, name)) return false;
|
||||
const DeclType = @TypeOf(@field(T, name));
|
||||
const decl_type_id = @typeId(DeclType);
|
||||
return decl_type_id == builtin.TypeId.Fn;
|
||||
return @typeId(DeclType) == .Fn;
|
||||
}
|
||||
};
|
||||
return Closure.trait;
|
||||
@ -80,11 +73,10 @@ test "std.meta.trait.hasFn" {
|
||||
pub fn hasField(comptime name: []const u8) TraitFn {
|
||||
const Closure = struct {
|
||||
pub fn trait(comptime T: type) bool {
|
||||
const info = @typeInfo(T);
|
||||
const fields = switch (info) {
|
||||
builtin.TypeId.Struct => |s| s.fields,
|
||||
builtin.TypeId.Union => |u| u.fields,
|
||||
builtin.TypeId.Enum => |e| e.fields,
|
||||
const fields = switch (@typeInfo(T)) {
|
||||
.Struct => |s| s.fields,
|
||||
.Union => |u| u.fields,
|
||||
.Enum => |e| e.fields,
|
||||
else => return false,
|
||||
};
|
||||
|
||||
@ -120,11 +112,11 @@ pub fn is(comptime id: builtin.TypeId) TraitFn {
|
||||
}
|
||||
|
||||
test "std.meta.trait.is" {
|
||||
testing.expect(is(builtin.TypeId.Int)(u8));
|
||||
testing.expect(!is(builtin.TypeId.Int)(f32));
|
||||
testing.expect(is(builtin.TypeId.Pointer)(*u8));
|
||||
testing.expect(is(builtin.TypeId.Void)(void));
|
||||
testing.expect(!is(builtin.TypeId.Optional)(anyerror));
|
||||
testing.expect(is(.Int)(u8));
|
||||
testing.expect(!is(.Int)(f32));
|
||||
testing.expect(is(.Pointer)(*u8));
|
||||
testing.expect(is(.Void)(void));
|
||||
testing.expect(!is(.Optional)(anyerror));
|
||||
}
|
||||
|
||||
pub fn isPtrTo(comptime id: builtin.TypeId) TraitFn {
|
||||
@ -138,9 +130,9 @@ pub fn isPtrTo(comptime id: builtin.TypeId) TraitFn {
|
||||
}
|
||||
|
||||
test "std.meta.trait.isPtrTo" {
|
||||
testing.expect(!isPtrTo(builtin.TypeId.Struct)(struct {}));
|
||||
testing.expect(isPtrTo(builtin.TypeId.Struct)(*struct {}));
|
||||
testing.expect(!isPtrTo(builtin.TypeId.Struct)(**struct {}));
|
||||
testing.expect(!isPtrTo(.Struct)(struct {}));
|
||||
testing.expect(isPtrTo(.Struct)(*struct {}));
|
||||
testing.expect(!isPtrTo(.Struct)(**struct {}));
|
||||
}
|
||||
|
||||
///////////Strait trait Fns
|
||||
@ -149,12 +141,10 @@ test "std.meta.trait.isPtrTo" {
|
||||
// Somewhat limited since we can't apply this logic to normal variables, fields, or
|
||||
// Fns yet. Should be isExternType?
|
||||
pub fn isExtern(comptime T: type) bool {
|
||||
const Extern = builtin.TypeInfo.ContainerLayout.Extern;
|
||||
const info = @typeInfo(T);
|
||||
return switch (info) {
|
||||
builtin.TypeId.Struct => |s| s.layout == Extern,
|
||||
builtin.TypeId.Union => |u| u.layout == Extern,
|
||||
builtin.TypeId.Enum => |e| e.layout == Extern,
|
||||
return switch (@typeInfo(T)) {
|
||||
.Struct => |s| s.layout == .Extern,
|
||||
.Union => |u| u.layout == .Extern,
|
||||
.Enum => |e| e.layout == .Extern,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
@ -169,12 +159,10 @@ test "std.meta.trait.isExtern" {
|
||||
}
|
||||
|
||||
pub fn isPacked(comptime T: type) bool {
|
||||
const Packed = builtin.TypeInfo.ContainerLayout.Packed;
|
||||
const info = @typeInfo(T);
|
||||
return switch (info) {
|
||||
builtin.TypeId.Struct => |s| s.layout == Packed,
|
||||
builtin.TypeId.Union => |u| u.layout == Packed,
|
||||
builtin.TypeId.Enum => |e| e.layout == Packed,
|
||||
return switch (@typeInfo(T)) {
|
||||
.Struct => |s| s.layout == .Packed,
|
||||
.Union => |u| u.layout == .Packed,
|
||||
.Enum => |e| e.layout == .Packed,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
@ -189,8 +177,8 @@ test "std.meta.trait.isPacked" {
|
||||
}
|
||||
|
||||
pub fn isUnsignedInt(comptime T: type) bool {
|
||||
return switch (@typeId(T)) {
|
||||
builtin.TypeId.Int => !@typeInfo(T).Int.is_signed,
|
||||
return switch (@typeInfo(T)) {
|
||||
.Int => |i| !i.is_signed,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
@ -203,9 +191,9 @@ test "isUnsignedInt" {
|
||||
}
|
||||
|
||||
pub fn isSignedInt(comptime T: type) bool {
|
||||
return switch (@typeId(T)) {
|
||||
builtin.TypeId.ComptimeInt => true,
|
||||
builtin.TypeId.Int => @typeInfo(T).Int.is_signed,
|
||||
return switch (@typeInfo(T)) {
|
||||
.ComptimeInt => true,
|
||||
.Int => |i| i.is_signed,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
@ -218,9 +206,8 @@ test "isSignedInt" {
|
||||
}
|
||||
|
||||
pub fn isSingleItemPtr(comptime T: type) bool {
|
||||
if (comptime is(builtin.TypeId.Pointer)(T)) {
|
||||
const info = @typeInfo(T);
|
||||
return info.Pointer.size == builtin.TypeInfo.Pointer.Size.One;
|
||||
if (comptime is(.Pointer)(T)) {
|
||||
return @typeInfo(T).Pointer.size == .One;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -233,9 +220,8 @@ test "std.meta.trait.isSingleItemPtr" {
|
||||
}
|
||||
|
||||
pub fn isManyItemPtr(comptime T: type) bool {
|
||||
if (comptime is(builtin.TypeId.Pointer)(T)) {
|
||||
const info = @typeInfo(T);
|
||||
return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Many;
|
||||
if (comptime is(.Pointer)(T)) {
|
||||
return @typeInfo(T).Pointer.size == .Many;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -249,9 +235,8 @@ test "std.meta.trait.isManyItemPtr" {
|
||||
}
|
||||
|
||||
pub fn isSlice(comptime T: type) bool {
|
||||
if (comptime is(builtin.TypeId.Pointer)(T)) {
|
||||
const info = @typeInfo(T);
|
||||
return info.Pointer.size == builtin.TypeInfo.Pointer.Size.Slice;
|
||||
if (comptime is(.Pointer)(T)) {
|
||||
return @typeInfo(T).Pointer.size == .Slice;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -264,15 +249,13 @@ test "std.meta.trait.isSlice" {
|
||||
}
|
||||
|
||||
pub fn isIndexable(comptime T: type) bool {
|
||||
if (comptime is(builtin.TypeId.Pointer)(T)) {
|
||||
const info = @typeInfo(T);
|
||||
if (info.Pointer.size == builtin.TypeInfo.Pointer.Size.One) {
|
||||
if (comptime is(builtin.TypeId.Array)(meta.Child(T))) return true;
|
||||
return false;
|
||||
if (comptime is(.Pointer)(T)) {
|
||||
if (@typeInfo(T).Pointer.size == .One) {
|
||||
return (comptime is(.Array)(meta.Child(T)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return comptime is(builtin.TypeId.Array)(T);
|
||||
return comptime is(.Array)(T);
|
||||
}
|
||||
|
||||
test "std.meta.trait.isIndexable" {
|
||||
@ -287,7 +270,7 @@ test "std.meta.trait.isIndexable" {
|
||||
|
||||
pub fn isNumber(comptime T: type) bool {
|
||||
return switch (@typeId(T)) {
|
||||
builtin.TypeId.Int, builtin.TypeId.Float, builtin.TypeId.ComptimeInt, builtin.TypeId.ComptimeFloat => true,
|
||||
.Int, .Float, .ComptimeInt, .ComptimeFloat => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
@ -307,9 +290,8 @@ test "std.meta.trait.isNumber" {
|
||||
}
|
||||
|
||||
pub fn isConstPtr(comptime T: type) bool {
|
||||
if (!comptime is(builtin.TypeId.Pointer)(T)) return false;
|
||||
const info = @typeInfo(T);
|
||||
return info.Pointer.is_const;
|
||||
if (!comptime is(.Pointer)(T)) return false;
|
||||
return @typeInfo(T).Pointer.is_const;
|
||||
}
|
||||
|
||||
test "std.meta.trait.isConstPtr" {
|
||||
@ -322,11 +304,8 @@ test "std.meta.trait.isConstPtr" {
|
||||
}
|
||||
|
||||
pub fn isContainer(comptime T: type) bool {
|
||||
const info = @typeInfo(T);
|
||||
return switch (info) {
|
||||
builtin.TypeId.Struct => true,
|
||||
builtin.TypeId.Union => true,
|
||||
builtin.TypeId.Enum => true,
|
||||
return switch (@typeId(T)) {
|
||||
.Struct, .Union, .Enum => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user