1 /*******************************************************************************
2
3 Utility to recursively reset fields of struct to their .init value while
4 preserving array pointers (their length is set to 0 but memory is kept
5 available for further reusage)
6
7 Copyright:
8 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
9 All rights reserved.
10
11 License:
12 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
13 Alternatively, this file may be distributed under the terms of the Tango
14 3-Clause BSD License (see LICENSE_BSD.txt for details).
15
16 *******************************************************************************/
17
18 module ocean.util.DeepReset;
19
20
21 import ocean.meta.types.Qualifiers;
22 import ocean.core.Array;
23 import ocean.meta.traits.Basic /* isArrayType */;
24
25 version (unittest)
26 {
27 import ocean.core.Test;
28 }
29
30 /*******************************************************************************
31
32 Template to determine the correct DeepReset function to call dependent on
33 the type given.
34
35 Params:
36 T = type to deep reset
37
38 Evaluates to:
39 aliases function appropriate to T
40
41 *******************************************************************************/
42
43 public template DeepReset ( T )
44 {
45 static if ( is(T == class) )
46 {
47 alias ClassDeepReset DeepReset;
48 }
49 else static if ( is(T == struct) )
50 {
51 alias StructDeepReset DeepReset;
52 }
53 else static if ( isArrayType!(T) == ArrayKind.Associative )
54 {
55 // TODO: reset associative arrays
56 pragma(msg, "Warning: deep reset of associative arrays not yet implemented");
57 alias nothing DeepReset;
58 }
59 else static if ( isArrayType!(T) == ArrayKind.Dynamic )
60 {
61 alias DynamicArrayDeepReset DeepReset;
62 }
63 else static if ( isArrayType!(T) == ArrayKind.Static )
64 {
65 alias StaticArrayDeepReset DeepReset;
66 }
67 else
68 {
69 pragma(msg, "Warning: DeepReset template could not expand for type " ~ T.stringof);
70 alias nothing DeepReset;
71 }
72 }
73
74
75
76 /*******************************************************************************
77
78 Deep reset function for dynamic arrays. To reset a dynamic array set the
79 length to 0.
80
81 Params:
82 T = type of array to deep copy
83 dst = destination array
84
85 *******************************************************************************/
86
87 public void DynamicArrayDeepReset ( T ) ( ref T[] dst )
88 {
89 ArrayDeepReset(dst);
90 dst.length = 0;
91 assumeSafeAppend(dst);
92 }
93
94
95
96 /*******************************************************************************
97
98 Deep reset function for static arrays. To reset a static array go through
99 the whole array and set the items to the init values for the type of the
100 array.
101
102 Params:
103 T = type of array to deep copy
104 dst = destination array
105
106 *******************************************************************************/
107
108 public void StaticArrayDeepReset ( T ) ( T[] dst )
109 {
110 ArrayDeepReset(dst);
111 }
112
113
114
115 /*******************************************************************************
116
117 Deep reset function for arrays.
118
119 Params:
120 T = type of array to deep copy
121 dst = destination array
122
123 *******************************************************************************/
124
125 private void ArrayDeepReset ( T ) ( ref T[] dst )
126 {
127 static if ( isArrayType!(T) == ArrayKind.Associative )
128 {
129 // TODO: copy associative arrays
130 pragma(msg, "Warning: deep reset of associative arrays not yet implemented");
131 }
132 else static if ( isArrayType!(T) )
133 {
134 foreach ( i, e; dst )
135 {
136 static if ( isArrayType!(T) == ArrayKind.Dynamic )
137 {
138 DynamicArrayDeepReset(dst[i]);
139 }
140 else
141 {
142 StaticArrayDeepReset(dst[i]);
143 }
144 }
145 }
146 else static if ( is(T == struct) )
147 {
148 foreach ( i, e; dst )
149 {
150 StructDeepReset(dst[i]);
151 }
152 }
153 else static if ( is(T == class) )
154 {
155 foreach ( i, e; dst )
156 {
157 ClassDeepReset(dst[i]);
158 }
159 }
160 else
161 {
162 // TODO this probably does not need to be done for a dynamic array
163 foreach ( ref item; dst )
164 {
165 item = item.init;
166 }
167 }
168 }
169
170
171
172 /*******************************************************************************
173
174 Deep reset function for structs.
175
176 Params:
177 T = type of struct to deep copy
178 dst = destination struct
179
180 *******************************************************************************/
181
182 // TODO: struct & class both share basically the same body, could be shared?
183
184 public void StructDeepReset ( T ) ( ref T dst )
185 {
186 static if ( !is(T == struct) )
187 {
188 static assert(false, "StructDeepReset: " ~ T.stringof ~ " is not a struct");
189 }
190
191 foreach ( i, member; dst.tupleof )
192 {
193 static if ( isArrayType!(typeof(member)) == ArrayKind.Associative )
194 {
195 // TODO: copy associative arrays
196 pragma(msg, "Warning: deep reset of associative arrays not yet implemented");
197 }
198 else static if ( isArrayType!(typeof(member)) == ArrayKind.Dynamic )
199 {
200 DynamicArrayDeepReset(dst.tupleof[i]);
201 }
202 else static if ( isArrayType!(typeof(member)) == ArrayKind.Static )
203 {
204 StaticArrayDeepReset(dst.tupleof[i]);
205 }
206 else static if ( is(typeof(member) == class) )
207 {
208 ClassDeepReset(dst.tupleof[i]);
209 }
210 else static if ( is(typeof(member) == struct) )
211 {
212 StructDeepReset(dst.tupleof[i]);
213 }
214 else
215 {
216 dst.tupleof[i] = dst.tupleof[i].init;
217 }
218 }
219 }
220
221
222
223 /*******************************************************************************
224
225 Deep reset function for dynamic class instances.
226
227 Params:
228 T = type of class to deep copy
229 dst = destination instance
230
231 *******************************************************************************/
232
233 public void ClassDeepReset ( T ) ( ref T dst )
234 {
235 static if ( !is(T == class) )
236 {
237 static assert(false, "ClassDeepReset: " ~ T.stringof ~ " is not a class");
238 }
239
240 foreach ( i, member; dst.tupleof )
241 {
242 static if ( isArrayType!(typeof(member)) == ArrayKind.Associative )
243 {
244 // TODO: copy associative arrays
245 pragma(msg, "Warning: deep reset of associative arrays not yet implemented");
246 }
247 else static if ( isArrayType!(typeof(member)) )
248 {
249 static if ( isArrayType!(typeof(member)) == ArrayKind.Dynamic )
250 {
251 DynamicArrayDeepReset(dst.tupleof[i]);
252 }
253 else
254 {
255 StaticArrayDeepReset(dst.tupleof[i]);
256 }
257 }
258 else static if ( is(typeof(member) == class) )
259 {
260 ClassDeepReset(dst.tupleof[i]);
261 }
262 else static if ( is(typeof(member) == struct) )
263 {
264 StructDeepReset(dst.tupleof[i]);
265 }
266 else
267 {
268 dst.tupleof[i] = dst.tupleof[i].init;
269 }
270 }
271
272 // Recurse into super any classes
273 static if ( is(T S == super ) )
274 {
275 foreach ( V; S )
276 {
277 static if ( !is(V == Object) )
278 {
279 ClassDeepReset(cast(V)dst);
280 }
281 }
282 }
283 }
284
285
286
287 /*******************************************************************************
288
289 unit test for the DeepReset method. Makes a test structure and fills it
290 with data before calling reset and making sure it is cleared.
291
292 We first build a basic struct that has both a single sub struct and a
293 dynamic array of sub structs. Both of these are then filled along with
294 the fursther sub sub struct.
295
296 The DeepReset method is then called. The struct is then confirmed to
297 have had it's members reset to the correct values
298
299 TODO Adjust the unit test so it also deals with struct being
300 re-initialised to make sure they are not full of old data (~=)
301
302 *******************************************************************************/
303
304
305 unittest
306 {
307 struct TestStruct
308 {
309 int a;
310 char[] b;
311 int[7] c;
312
313 public struct SubStruct
314 {
315 int d;
316 char[] e;
317 char[][] f;
318 int[7] g;
319
320 public struct SubSubStruct
321 {
322 int h;
323 char[] i;
324 char[][] j;
325 int[7] k;
326
327 void InitStructure()
328 {
329 this.h = -52;
330 this.i.copy("even even more test text");
331 this.j.length = 3;
332 this.j[0].copy("abc");
333 this.j[1].copy("def");
334 this.j[2].copy("ghi");
335 foreach ( ref item; this.k )
336 {
337 item = 120000;
338 }
339 }
340 }
341
342 void InitStructure()
343 {
344 this.d = 32;
345 this.e.copy("even more test text");
346
347 this.f.length = 1;
348 this.f[0].copy("abc");
349 foreach ( ref item; this.g )
350 {
351 item = 32400;
352 }
353 }
354
355 SubSubStruct[] sub_sub_struct;
356 }
357
358 SubStruct sub_struct;
359
360 SubStruct[] sub_struct_array;
361 }
362
363 TestStruct test_struct;
364 test_struct.a = 7;
365 test_struct.b.copy("some test");
366 foreach ( i, ref item; test_struct.c )
367 {
368 item = 64800;
369 }
370
371 TestStruct.SubStruct sub_struct;
372 sub_struct.InitStructure;
373 test_struct.sub_struct = sub_struct;
374 test_struct.sub_struct_array ~= sub_struct;
375 test_struct.sub_struct_array ~= sub_struct;
376
377
378 TestStruct.SubStruct.SubSubStruct sub_sub_struct;
379 sub_sub_struct.InitStructure;
380 test_struct.sub_struct_array[0].sub_sub_struct ~= sub_sub_struct;
381 test_struct.sub_struct_array[1].sub_sub_struct ~= sub_sub_struct;
382 test_struct.sub_struct_array[1].sub_sub_struct ~= sub_sub_struct;
383 test_struct.sub_struct.sub_sub_struct ~= sub_sub_struct;
384 test_struct.sub_struct.sub_sub_struct ~= sub_sub_struct;
385
386 DeepReset!(TestStruct)(test_struct);
387
388 test!("==")(test_struct.a, 0);
389 test!("==")(test_struct.b, ""[]);
390 foreach ( item; test_struct.c )
391 {
392 test!("==")(item, 0);
393 }
394
395 test!("==")(test_struct.sub_struct_array.length, 0);
396
397 test!("==")(test_struct.sub_struct.d, 0);
398 test!("==")(test_struct.sub_struct.e, ""[]);
399 test!("==")(test_struct.sub_struct.f.length, 0);
400 foreach ( item; test_struct.sub_struct.g )
401 {
402 test!("==")(item, 0);
403 }
404
405 test!("==")(test_struct.sub_struct.sub_sub_struct.length, 0);
406
407 //Test nested classes.
408 class TestClass
409 {
410 int a;
411 char[] b;
412 int[2] c;
413
414 public class SubClass
415 {
416 int d;
417 char[] e;
418 }
419
420 SubClass s;
421 }
422
423 TestClass test_class = new TestClass;
424 test_class.s = test_class..new SubClass;
425 test_class.a = 7;
426 test_class.b = [];
427 test_class.b ~= 't';
428 test_class.c[1] = 1;
429 test_class.s.d = 5;
430 test_class.s.e = [];
431 test_class.s.e ~= 'q';
432
433 DeepReset!(TestClass)(test_class);
434 test!("==")(test_class.a, 0);
435 test!("==")(test_class.b.length, 0);
436 test!("==")(test_class.c[1], 0);
437 test!("!is")(cast(void*)test_class.s, null);
438 test!("==")(test_class.s.d, 0);
439 test!("==")(test_class.s.e.length, 0);
440 }