1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
//
// Zig has builtins for mathematical operations such as...
//
// @sqrt @sin @cos
// @exp @log @floor
//
// ...and lots of type casting operations such as...
//
// @as @intToError @intToFloat
// @intToPtr @ptrToInt @enumToInt
//
// Spending part of a rainy day skimming through the complete
// list of builtins in the official Zig documentation wouldn't be
// a bad use of your time. There are some seriously cool features
// in there. Check out @call, @compileLog, @embedFile, and @src!
//
// ...
//
// For now, we're going to complete our examination of builtins
// by exploring just THREE of Zig's MANY introspection abilities:
//
// 1. @This() type
//
// Returns the innermost struct, enum, or union that a function
// call is inside.
//
// 2. @typeInfo(comptime T: type) @import("std").builtin.TypeInfo
//
// Returns information about any type in a TypeInfo union which
// will contain different information depending on which type
// you're examining.
//
// 3. @TypeOf(...) type
//
// Returns the type common to all input parameters (each of which
// may be any expression). The type is resolved using the same
// "peer type resolution" process the compiler itself uses when
// inferring types.
//
// (Notice how the two functions which return types start with
// uppercase letters? This is a standard naming practice in Zig.)
//
const print = @import("std").debug.print;
const Narcissus = struct {
me: *Narcissus = undefined,
myself: *Narcissus = undefined,
echo: void = undefined, // Alas, poor Echo!
fn fetchTheMostBeautifulType() type {
return @This();
}
};
pub fn main() void {
var narcissus: Narcissus = Narcissus{};
// Oops! We cannot leave the 'me' and 'myself' fields
// undefined. Please set them here:
narcissus.me = &narcissus;
narcissus.??? = ???;
// This determines a "peer type" from three separate
// references (they just happen to all be the same object).
const Type1 = @TypeOf(narcissus, narcissus.me.*, narcissus.myself.*);
// Oh dear, we seem to have done something wrong when calling
// this function. We called it as a method, which would work
// if it had a self parameter. But it doesn't. (See above.)
//
// The fix for this is very subtle, but it makes a big
// difference!
const Type2 = narcissus.fetchTheMostBeautifulType();
// Now we print a pithy statement about Narcissus.
print("A {s} loves all {s}es. ", .{
maximumNarcissism(Type1),
maximumNarcissism(Type2),
});
// His final words as he was looking in
// those waters he habitually watched
// were these:
// "Alas, my beloved boy, in vain!"
// The place gave every word back in reply.
// He cried:
// "Farewell."
// And Echo called:
// "Farewell!"
//
// --Ovid, The Metamorphoses
// translated by Ian Johnston
print("He has room in his heart for:", .{});
// A StructFields array
const fields = @typeInfo(Narcissus).Struct.fields;
// 'fields' is a slice of StructFields. Here's the declaration:
//
// pub const StructField = struct {
// name: []const u8,
// field_type: type,
// default_value: anytype,
// is_comptime: bool,
// alignment: comptime_int,
// };
//
// Please complete these 'if' statements so that the field
// name will not be printed if the field is of type 'void'
// (which is a zero-bit type that takes up no space at all!):
if (fields[0].??? != void) {
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[0].name});
}
if (fields[1].??? != void) {
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[1].name});
}
if (fields[2].??? != void) {
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[2].name});
}
// Yuck, look at all that repeated code above! I don't know
// about you, but it makes me itchy.
//
// Alas, we can't use a regular 'for' loop here because
// 'fields' can only be evaluated at compile time. It seems
// like we're overdue to learn about this "comptime" stuff,
// doesn't it? Don't worry, we'll get there.
print(".\n", .{});
}
// NOTE: This exercise did not originally include the function below.
// But a change after Zig 0.10.0 added the source file name to the
// type. "Narcissus" became "065_builtins2.Narcissus".
//
// To fix this, I've added this function to strip the filename from
// the front of the type name in the dumbest way possible. (It returns
// a slice of the type name starting at character 14 (assuming
// single-byte characters).
//
// We'll be seeing @typeName again in Exercise 070. For now, you can
// see that it takes a Type and returns a u8 "string".
fn maximumNarcissism(myType: anytype) []const u8 {
// Turn '065_builtins2.Narcissus' into 'Narcissus'
return @typeName(myType)[14..];
}
|