authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-20 16:04:03-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-20 16:04:03-04:00
log820bf054ea01070b64d6cabf2f20e747909e2611
tree14314c97268178ec760d6b9849c08e18c680ef18
parentb8ce8f219c48ae833199130ce575241f848d690b

std.fmt.format: handle non-pointer struct/union/enum

Also adds support for printing structs via reflection. The case when structs have pointers to themselves is not handled yet. closes #1380

1 files changed, 50 insertions(+), 31 deletions(-)

std/fmt/index.zig+50-31
...@@ -146,6 +146,40 @@ pub fn formatType(...@@ -146,6 +146,40 @@ pub fn formatType(
146 builtin.TypeId.Promise => {146 builtin.TypeId.Promise => {
147 return format(context, Errors, output, "promise@{x}", @ptrToInt(value));147 return format(context, Errors, output, "promise@{x}", @ptrToInt(value));
148 },148 },
149 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
150 const has_cust_fmt = comptime cf: {
151 const info = @typeInfo(T);
152 const defs = switch (info) {
153 builtin.TypeId.Struct => |s| s.defs,
154 builtin.TypeId.Union => |u| u.defs,
155 builtin.TypeId.Enum => |e| e.defs,
156 else => unreachable,
157 };
158
159 for (defs) |def| {
160 if (mem.eql(u8, def.name, "format")) {
161 break :cf true;
162 }
163 }
164 break :cf false;
165 };
166
167 if (has_cust_fmt) return value.format(fmt, context, Errors, output);
168 try output(context, @typeName(T));
169 comptime var field_i = 0;
170 inline while (field_i < @memberCount(T)) : (field_i += 1) {
171 if (field_i == 0) {
172 try output(context, "{ .");
173 } else {
174 try output(context, ", .");
175 }
176 try output(context, @memberName(T, field_i));
177 try output(context, " = ");
178 try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output);
179 }
180 try output(context, " }");
181 return;
182 },
149 builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {183 builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
150 builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {184 builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {
151 builtin.TypeId.Array => |info| {185 builtin.TypeId.Array => |info| {
...@@ -155,25 +189,7 @@ pub fn formatType(...@@ -155,25 +189,7 @@ pub fn formatType(
155 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));189 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
156 },190 },
157 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {191 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
158 const has_cust_fmt = comptime cf: {192 return formatType(value.*, fmt, context, Errors, output);
159 const info = @typeInfo(T.Child);
160 const defs = switch (info) {
161 builtin.TypeId.Struct => |s| s.defs,
162 builtin.TypeId.Union => |u| u.defs,
163 builtin.TypeId.Enum => |e| e.defs,
164 else => unreachable,
165 };
166
167 for (defs) |def| {
168 if (mem.eql(u8, def.name, "format")) {
169 break :cf true;
170 }
171 }
172 break :cf false;
173 };
174
175 if (has_cust_fmt) return value.format(fmt, context, Errors, output);
176 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
177 },193 },
178 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),194 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
179 },195 },
...@@ -911,14 +927,12 @@ test "fmt.format" {...@@ -911,14 +927,12 @@ test "fmt.format" {
911 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));927 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
912 try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));928 try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
913 {929 {
914 // Dummy field because of https://github.com/ziglang/zig/issues/557.
915 const Struct = struct {930 const Struct = struct {
916 unused: u8,931 field: u8,
917 };932 };
918 var buf1: [32]u8 = undefined;933 const value = Struct{ .field = 42 };
919 const value = Struct{ .unused = 42 };934 try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", value);
920 const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);935 try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", &value);
921 assert(mem.startsWith(u8, result, "pointer: Struct@"));
922 }936 }
923 {937 {
924 var buf1: [32]u8 = undefined;938 var buf1: [32]u8 = undefined;
...@@ -941,6 +955,7 @@ test "fmt.format" {...@@ -941,6 +955,7 @@ test "fmt.format" {
941 {955 {
942 // This fails on release due to a minor rounding difference.956 // This fails on release due to a minor rounding difference.
943 // --release-fast outputs 9.999960000000001e-40 vs. the expected.957 // --release-fast outputs 9.999960000000001e-40 vs. the expected.
958 // TODO fix this, it should be the same in Debug and ReleaseFast
944 if (builtin.mode == builtin.Mode.Debug) {959 if (builtin.mode == builtin.Mode.Debug) {
945 var buf1: [32]u8 = undefined;960 var buf1: [32]u8 = undefined;
946 const value: f64 = 9.999960e-40;961 const value: f64 = 9.999960e-40;
...@@ -1133,23 +1148,23 @@ test "fmt.format" {...@@ -1133,23 +1148,23 @@ test "fmt.format" {
1133 y: f32,1148 y: f32,
11341149
1135 pub fn format(1150 pub fn format(
1136 self: *SelfType,1151 self: SelfType,
1137 comptime fmt: []const u8,1152 comptime fmt: []const u8,
1138 context: var,1153 context: var,
1139 comptime Errors: type,1154 comptime Errors: type,
1140 output: fn (@typeOf(context), []const u8) Errors!void,1155 output: fn (@typeOf(context), []const u8) Errors!void,
1141 ) Errors!void {1156 ) Errors!void {
1142 if (fmt.len > 0) {1157 switch (fmt.len) {
1143 if (fmt.len > 1) unreachable;1158 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
1144 switch (fmt[0]) {1159 1 => switch (fmt[0]) {
1145 //point format1160 //point format
1146 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),1161 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
1147 //dimension format1162 //dimension format
1148 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),1163 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
1149 else => unreachable,1164 else => unreachable,
1150 }1165 },
1166 else => unreachable,
1151 }1167 }
1152 return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y);
1153 }1168 }
1154 };1169 };
11551170
...@@ -1160,6 +1175,10 @@ test "fmt.format" {...@@ -1160,6 +1175,10 @@ test "fmt.format" {
1160 };1175 };
1161 try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);1176 try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);
1162 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value);1177 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value);
1178
1179 // same thing but not passing a pointer
1180 try testFmt("point: (10.200,2.220)\n", "point: {}\n", value);
1181 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
1163 }1182 }
1164}1183}
11651184