Skip to content

Instantly share code, notes, and snippets.

@garettbass
Created June 22, 2022 03:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garettbass/5a1bcad4da4b5a85754deeedf840ca72 to your computer and use it in GitHub Desktop.
Save garettbass/5a1bcad4da4b5a85754deeedf840ca72 to your computer and use it in GitHub Desktop.
Various utility functions for initialization in Zig
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