authorgravatar for brodeuralexis@gmail.comAlexis Brodeur <brodeuralexis@gmail.com> 2020-05-29 18:25:21-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-01 14:45:35-04:00
logc0e5eca6f2e1a688a6703ed36aef300d9393183d
treea641af417f7e99f69083a8a2910d4d0c801ee86d
parent937dcad0b3802863af0891b2766acb55f89949ba

Add initialization helper

When using C libraries, C99 designator list initialization is often times used to initialize data structure. While `std.mem.zeroes` and manually assigning to each field can achieve the same result, it is much more verbose then the equivalent C code: ```zig usingnamespace @cImport({ @cInclude("sokol_app.h"); }); // Using `std.mem.zeroes` and manual assignment. var app_desc = std.mem.zeroes(sapp_desc); app_desc.init_cb = init; app_desc.frame_cb = frame; app_desc.cleanup_cb = cleanup; app_desc.width = 400; app_desc.height = 300; app_desc.window_name = "no default init"; // Using `std.mem.defaultInit`. var app_desc = std.mem.defaultInit(sapp_desc, .{ .init_cb = init, .frame_cb = frame, .cleanup_cb = cleanup, .width = 400, .height = 300, .window_name = "default init" }); ``` The `std.mem.defaultInit` aims to solve this problem by zero initializing all fields of the given struct to their zero, or default value if any. Each field mentionned in the `init` variable is then assigned to the corresponding field in the struct. If a field is a struct, and an initializer for it is present, it is recursively initialized.

1 files changed, 74 insertions(+), 0 deletions(-)

lib/std/mem.zig+74
...@@ -516,6 +516,80 @@ test "mem.secureZero" {...@@ -516,6 +516,80 @@ test "mem.secureZero" {
516 testing.expectEqualSlices(u8, a[0..], b[0..]);516 testing.expectEqualSlices(u8, a[0..], b[0..]);
517}517}
518518
519/// Initializes all fields of the struct with their default value, or zero values if no default value is present.
520/// If the field is present in the provided initial values, it will have that value instead.
521/// Structs are initialized recursively.
522pub fn defaultInit(comptime T: type, init: var) T {
523 comptime const Init = @TypeOf(init);
524
525 switch (@typeInfo(T)) {
526 .Struct => |struct_info| {
527 switch (@typeInfo(Init)) {
528 .Struct => |init_info| {
529 var value = std.mem.zeroes(T);
530
531 inline for (init_info.fields) |field| {
532 if (!@hasField(T, field.name)) {
533 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
534 }
535 }
536
537 inline for (struct_info.fields) |field| {
538 if (@hasField(Init, field.name)) {
539 switch (@typeInfo(field.field_type)) {
540 .Struct => {
541 @field(value, field.name) = defaultInit(field.field_type, @field(init, field.name));
542 },
543 else => {
544 @field(value, field.name) = @field(init, field.name);
545 },
546 }
547 } else if (field.default_value != null) {
548 @field(value, field.name) = field.default_value;
549 }
550 }
551
552 return value;
553 },
554 else => {
555 @compileError("The initializer must be a struct");
556 },
557 }
558 },
559 else => {
560 @compileError("Can't default init a " ++ @typeName(T));
561 },
562 }
563}
564
565test "mem.defaultInit" {
566 const I = struct {
567 d: f64,
568 };
569
570 const S = struct {
571 a: u32,
572 b: ?bool,
573 c: I,
574 e: [3]u8,
575 f: i64,
576 };
577
578 const s = defaultInit(S, .{
579 .a = 42,
580 });
581
582 testing.expectEqual(s, S{
583 .a = 42,
584 .b = null,
585 .c = .{
586 .d = 0,
587 },
588 .e = [3]u8{0, 0, 0},
589 .f = 0,
590 });
591}
592
519pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order {593pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order {
520 const n = math.min(lhs.len, rhs.len);594 const n = math.min(lhs.len, rhs.len);
521 var i: usize = 0;595 var i: usize = 0;