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 }