-
Notifications
You must be signed in to change notification settings - Fork 414
/
specialMethods.chpl
216 lines (171 loc) · 6.29 KB
/
specialMethods.chpl
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
// Special Methods
/* This primer covers special methods for classes and records */
// Classes and records can have a handful of methods that are specially named
// and treated differently than other methods. These methods enable
// operations such as initializing or deinitializing a class or record
// instance, accessing a class or record as though it were an array,
// iterating over an class or record, and defining the way a class or record
// is read from or written to a channel.
//
// Note that there are two ways to declare a method. Methods declared within
// a class or record are called primary methods:
record ExampleRecord1 {
var exampleField: int;
proc primaryMethod() { }
}
// Methods declared outside of a class or record are called secondary methods:
record ExampleRecord2 {
var exampleField: int;
}
proc ExampleRecord2.secondaryMethod() { }
// Methods declared outside of a class or record and outside of the scope where
// the type is defined are called tertiary methods. Tertiary methods follow the
// same declaration syntax as secondary methods.
// This primer will use the secondary method form, but all of the special
// methods can also be written as primary methods.
// First we will declare a simple record with a field that is a tuple of
// integers. We'll add special methods and iterators to this record later.
// To make the language recognize a special method, it's necessary to implement
// the corresponding interface. For the ``hash`` method we'll see below, the
// appropriate interface is ``hashable``. We can mark ``R`` as implementing
// ``hashable`` by including a ``: hashable`` after its name when we declare it.
record R : hashable, writeSerializable, readDeserializable {
param size: int = 10;
var vals: size*int;
}
/*
Initializers and Deinitializers
-------------------------------
*/
// An initializer named ``init`` is called when creating an instance of the
// class or record, for example with the ``new`` keyword. An initializer
// accepting zero arguments is called a *default initializer*.
// A method named ``init=`` is called a *copy initializer* and accepts a
// single argument.
// If a method named ``postinit`` that accepts zero arguments exists for a
// class or record type, it will automatically be called after the
// initializer call completes.
// The ``deinit`` method will deinitialize a record when it leaves scope,
// or a class when ``delete`` is called on it. If the class or record
// contained any unmanaged classes, open files, etc. this method would be
// the place to delete them or otherwise clean them up. See the :doc:`records`
// primer for more details on initializers and deinitializers.
/*
The ``this`` Accessor
---------------------
*/
// The ``this`` method gives the record the ability to be accessed like an
// array. Here we use the argument as an index to choose a tuple element.
proc ref R.this(n: int) ref {
if !vals.indices.contains(n) then
halt("index out of bounds accessing R");
return vals[n];
}
// All functions and methods in Chapel can be called using either parenthesis
// or square brackets. Here we'll access the record by implicitly calling
// the ``this`` method defined above.
var r = new R();
r[0] = 1;
r(2) = 3;
writeln(r.vals);
/*
Default Iterators
-----------------
*/
// An iterator named ``these`` that can accept zero arguments is automatically
// called when a record or class instance is used in the iterator position
// of a ``for`` loop.
iter ref R.these() ref {
for i in vals.indices {
yield vals[i];
}
}
for val in r {
val += 1;
}
writeln(r.vals);
// Classes and records can also define parallel iterators including
// leader/follower iterator pairs and standalone parallel iterators. For
// more information on parallel iterators, see the :doc:`parIters` primer.
/*
Custom Hashing
--------------
*/
// By default, the compiler will define a hash method for any record
// that does not define its own ``==`` or ``!=`` overloads. This
// permits such records to be used as the indices of associative
// domains, the values in a :mod:`Set`, or the keys in a :mod:`Map`.
// Users can override this default by supplying their own hash method
// that returns a ``uint`` or ``int`` value. For example:
use Map;
proc R.hash(): uint {
writeln("In custom hash function");
return vals[0] : uint;
}
// Now that the record R has a ``hash`` method defined, Chapel's,
// ``set``, ``map``, and associative domain types will call this
// custom ``hash`` instead of the compiler-generated method.
var myMap = new map(R, int);
var myD: domain(R);
var myR = new R();
myMap[myR] = 5;
myD += myR;
/*
IO Methods
----------
*/
// The ``serialize`` method defines how to write an instance of R to a
// channel. We'll write the ``vals`` tuple between asterisks. See section
// :ref:`serialize-deserialize` for more information on the ``serialize`` and
// ``deserialize`` methods.
use IO; // required for file operations
config const filename = "tempfile.txt";
proc R.serialize(writer: fileWriter(?),
ref serializer: writer.serializerType) throws {
writer.write("*", vals, "*");
}
{
// Open the fileWriter in a new block so that deinitializers
// will close it at the end of the block
var fw = openWriter(filename);
fw.writeln(r);
}
// The ``deserialize`` method defines how to read an instance of R from a
// channel. We'll read the ``vals`` tuple between asterisks like how it
// was written above.
proc ref R.deserialize(reader: fileReader(?),
ref deserializer: reader.deserializerType) throws {
reader.readLiteral("*");
reader.read(vals);
reader.readLiteral("*");
}
{
var fr = openReader(filename);
var r2 = new R();
fr.readln(r2);
assert(r == r2);
}
{
var fw = openWriter(filename);
fw.writeln(r);
fw.flush();
writeln(r);
var r2 = new R();
var fr = openReader(filename);
fr.readln(r2);
assert(r == r2);
}
// Clean up the temporary file we created earlier.
{
use FileSystem;
if exists(filename) then
remove(filename);
}
/*
Operator Overloads
------------------
*/
// Operators can be overloaded for record types to support
// assignment (``=``), comparisons, (``<``, ``<=``, ``>``, ``>=``, ``==``,
// ``!=``), and other general operators (``+``, ``-``, ``*``, ``/``, ...).
// These are declared as regular functions using the ``operator`` keyword.