Created
June 22, 2022 03:47
-
-
Save garettbass/5a1bcad4da4b5a85754deeedf840ca72 to your computer and use it in GitHub Desktop.
Various utility functions for initialization in Zig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const Elem = std.meta.Elem; | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
fn Default(comptime T: type) type { | |
return struct { | |
const value = default(T); | |
}; | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
pub fn zero(comptime T: type) T { | |
var result: T = undefined; | |
std.mem.set(u8, std.mem.asBytes(&result), @as(u8, 0)); | |
return result; | |
} | |
test "zero" { | |
const UnspecifiedStruct = struct { | |
field0: bool, | |
field1: u32, | |
field2: i32, | |
field3: f32, | |
field4: @Vector(4, f32), | |
field5: [4]f32, | |
}; | |
const UndefinedStruct = struct { | |
field0: bool = undefined, | |
field1: u32 = undefined, | |
field2: i32 = undefined, | |
field3: f32 = undefined, | |
field4: @Vector(4, f32) = undefined, | |
field5: [4]f32 = undefined, | |
}; | |
const DefaultedStruct = struct { | |
field0: bool = true, | |
field1: u32 = 1, | |
field2: i32 = 1, | |
field3: f32 = 1, | |
field4: @Vector(4, f32) = [_]f32{ 1, 1, 1, 1 }, | |
field5: [4]f32 = [_]f32{ 1, 1, 1, 1 }, | |
}; | |
const print = std.debug.print; | |
print("\n", .{}); | |
print(" zero(bool): {}\n", .{zero(bool)}); | |
print(" zero(u32): {}\n", .{zero(u32)}); | |
print(" zero(i32): {}\n", .{zero(i32)}); | |
print(" zero(u32): {}\n", .{zero(u32)}); | |
print(" zero(@Vector(4, f32)): {}\n", .{zero(@Vector(4, f32))}); | |
print(" zero([4]f32): {any}\n", .{zero([4]f32)}); | |
print(" zero(UnspecifiedStruct): {}\n", .{zero(UnspecifiedStruct)}); | |
print(" zero(UndefinedStruct): {}\n", .{zero(UndefinedStruct)}); | |
print(" zero(DefaultedStruct): {}\n", .{zero(DefaultedStruct)}); | |
print("\n", .{}); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
pub fn default(comptime T: type) T { | |
switch (@typeInfo(T)) { | |
.Bool => return false, | |
.Int, .Float => return @as(T, 0), | |
.Enum => return @intToEnum(T, 0), | |
.Vector => |v| { | |
const len = v.len; | |
const ComponentType = v.child; | |
const result: T = [1]ComponentType{ default(ComponentType) } ** len; | |
return result; | |
}, | |
.Array => |a| { | |
if (a.sentinel != null) { | |
@compileError("TODO: handle sentinel"); | |
} | |
const len = a.len; | |
const ElementType = a.child; | |
const result: T = [1]ElementType{ default(ElementType) } ** len; | |
return result; | |
}, | |
.Union => |u| { | |
var result: T = zero(T); // zero-initialize padding | |
inline for (u.fields) |field| { | |
const FieldType = field.field_type; | |
if (field.default_value) |opaque_ptr| { | |
const concrete_ptr = @ptrCast(*const FieldType, opaque_ptr); | |
@field(result, field.name) = concrete_ptr.*; | |
} else { | |
@field(result, field.name) = default(FieldType); | |
} | |
break; // only initialize the first field | |
} | |
return result; | |
}, | |
.Struct => |s| { | |
var result: T = zero(T); // zero-initialize padding | |
inline for (s.fields) |field| { | |
const FieldType = field.field_type; | |
if (field.default_value) |opaque_ptr| { | |
const concrete_ptr = @ptrCast(*const FieldType, opaque_ptr); | |
@field(result, field.name) = concrete_ptr.*; | |
} else { | |
@field(result, field.name) = default(FieldType); | |
} | |
} | |
return result; | |
}, | |
.Optional => { | |
const result: T = null; | |
return result; | |
}, | |
.Pointer => |p| { | |
const TargetType = p.child; | |
const result: T = &Default(TargetType).value; | |
return result; | |
}, | |
else => { | |
@compileError("TODO: default(" ++ @typeName(T) ++ ") not implemented"); | |
}, | |
} | |
} | |
test "default" { | |
const UnspecifiedStruct = struct { | |
field0: bool, | |
field1: u32, | |
field2: i32, | |
field3: f32, | |
field4: @Vector(4, f32), | |
field5: [4]f32, | |
}; | |
const UndefinedStruct = struct { | |
field0: bool = undefined, | |
field1: u32 = undefined, | |
field2: i32 = undefined, | |
field3: f32 = undefined, | |
field4: @Vector(4, f32) = undefined, | |
field5: [4]f32 = undefined, | |
}; | |
const DefaultedStruct = struct { | |
field0: bool = true, | |
field1: u32 = 1, | |
field2: i32 = 1, | |
field3: f32 = 1, | |
field4: @Vector(4, f32) = [_]f32{ 1, 1, 1, 1 }, | |
field5: [4]f32 = [_]f32{ 1, 1, 1, 1 }, | |
}; | |
const print = std.debug.print; | |
print("\n", .{}); | |
print(" default(bool): {}\n", .{default(bool)}); | |
print(" default(u32): {}\n", .{default(u32)}); | |
print(" default(i32): {}\n", .{default(i32)}); | |
print(" default(u32): {}\n", .{default(u32)}); | |
print(" default(@Vector(4, f32)): {}\n", .{default(@Vector(4, f32))}); | |
print(" default([4]f32): {any}\n", .{default([4]f32)}); | |
print(" default(UnspecifiedStruct): {}\n", .{default(UnspecifiedStruct)}); | |
print(" default(UndefinedStruct): {}\n", .{default(UndefinedStruct)}); | |
print(" default(DefaultedStruct): {}\n", .{default(DefaultedStruct)}); | |
print("\n", .{}); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
pub fn isDefault(t: anytype) bool { | |
const T = @TypeOf(t); | |
var t_bytes = std.mem.asBytes(&t); | |
var default_bytes = std.mem.asBytes(&Default(T).value); | |
return std.mem.eql(u8, t_bytes, default_bytes); | |
} | |
test "isDefault" { | |
// const UndefinedStruct = struct { | |
// field0: bool = undefined, | |
// field1: u32 = undefined, | |
// field2: i32 = undefined, | |
// field3: f32 = undefined, | |
// field4: @Vector(4, f32) = undefined, | |
// field5: [4]f32 = undefined, | |
// }; | |
// const DefaultedStruct = struct { | |
// field0: bool = true, | |
// field1: u32 = 1, | |
// field2: i32 = 1, | |
// field3: f32 = 1, | |
// field4: @Vector(4, f32) = [_]f32{ 1, 1, 1, 1 }, | |
// field5: [4]f32 = [_]f32{ 1, 1, 1, 1 }, | |
// }; | |
const print = std.debug.print; | |
print("\n", .{}); | |
print(" isDefault(false): {}\n", .{isDefault(false)}); | |
print(" isDefault(true): {}\n", .{isDefault(true)}); | |
print(" isDefault(@as(u32, 0)): {}\n", .{isDefault(@as(u32, 0))}); | |
print(" isDefault(@as(u32, 1)): {}\n", .{isDefault(@as(u32, 1))}); | |
// print(" isDefault(UndefinedStruct{}): {}\n", .{isDefault(UndefinedStruct{})}); | |
// print(" isDefault(DefaultedStruct{}): {}\n", .{isDefault(DefaultedStruct{})}); | |
print("\n", .{}); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
pub fn sliceToArray(comptime T: type, comptime elements: []const Elem(T)) T { | |
switch (@typeInfo(T)) { | |
.Array => |a| { | |
if (a.sentinel != null) { | |
@compileError("TODO: handle sentinel"); | |
} | |
const array_len: usize = a.len; | |
const slice_len: usize = elements.len; | |
if (array_len < slice_len) { | |
@compileError("too many elements for " ++ @typeName(T)); | |
} | |
const ElementType = a.child; | |
var result: T = undefined; | |
var i: usize = 0; | |
while (i < slice_len) : (i += 1) { | |
result[i] = elements[i]; | |
} | |
while (i < array_len) : (i += 1) { | |
result[i] = default(ElementType); | |
} | |
return result; | |
}, | |
else => { | |
@compileError("expected: array type, found: " ++ @typeName(T)); | |
}, | |
} | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
const Foo = struct { | |
b: bool, | |
u: u32, | |
f: f32, | |
}; | |
const Foox4 = [4]Foo; | |
test "sliceToArray" { | |
const foos: Foox4 = sliceToArray(Foox4, &[_]Foo{ | |
Foo{ .b = true, .u = 1, .f = 1 }, | |
Foo{ .b = true, .u = 2, .f = 2 }, | |
Foo{ .b = true, .u = 3, .f = 3 }, | |
// final element will be default-initialized | |
}); | |
const print = std.debug.print; | |
print("\n", .{}); | |
print(" foos: {any}\n", .{foos}); | |
print("\n", .{}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment