1 /******************************************************************************* 2 3 Generic utility allowing to recursively visit every field in an aggregate 4 and possibly those indirectly reachable from them. 5 6 Intended as a common implementation base for various more domain-specific 7 utilities. 8 9 Copyright: 10 Copyright (c) 2018 dunnhumby Germany GmbH. 11 All rights reserved. 12 13 License: 14 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 15 Alternatively, this file may be distributed under the terms of the Tango 16 3-Clause BSD License (see LICENSE_BSD.txt for details). 17 18 *******************************************************************************/ 19 20 module ocean.meta.values.VisitValue; 21 22 import ocean.meta.traits.Basic; 23 import ocean.meta.types.Arrays; 24 import ocean.meta.types.Typedef; 25 26 /******************************************************************************* 27 28 Recursively iterates over `value` fields and calls `visitor.visit` for each 29 one of those. `visitor.visit` is expected to be a template with the 30 following signature: 31 32 `bool visit ( T ) ( T* value )` 33 34 The return value indicates whether the value should be recursed into or 35 not. 36 37 Example: 38 39 --- 40 struct SumIntegersVisitor 41 { 42 /// Visitor can have any internal state it may need 43 long sum; 44 45 /// Returns: 'true' if recursion shall continue for this type 46 bool visit ( T ) ( T* value ) 47 { 48 static if (isIntegerType!(T)) 49 sum += *value; 50 51 return true; 52 } 53 } 54 55 SumIntegersVisitor visitor; 56 visitValue(some_value, visitor); 57 --- 58 59 Params: 60 value = root value to visit 61 visitor = struct providing templated `visit` method accepting single 62 argument which will be a pointer to visited element 63 64 *******************************************************************************/ 65 66 public void visitValue ( T, Visitor ) ( ref T value, ref Visitor visitor ) 67 { 68 VisitImpl!(Visitor) impl; 69 impl.visitor = &visitor; 70 impl.visitAll(&value); 71 } 72 73 /******************************************************************************* 74 75 Implementation of visiting logic. Some types are currently not supported 76 because it is not clear what would recursing mean for them. 77 78 Params: 79 Visitor = user-provided struct implementing templated `visit` method 80 to be called for individual values 81 82 *******************************************************************************/ 83 84 private struct VisitImpl ( Visitor ) 85 { 86 /// Refers to the persistent instance of visitor struct in case 87 /// implementation / may need internal state 88 Visitor* visitor; 89 90 /*************************************************************************** 91 92 Main recursive type/value visiting implementation 93 94 Params: 95 value = pointer to currently processed value. Pointer is used 96 instead of a reference because D1 does not allow defining 97 a reference to a static array 98 99 ***************************************************************************/ 100 101 private void visitAll ( T ) ( T* value ) 102 { 103 auto deeper = this.visitor.visit(value); 104 105 if (!deeper) 106 return; 107 108 static if (isPrimitiveType!(T)) 109 { 110 // do nothing, already processed 111 } 112 else static if (isTypedef!(T)) 113 { 114 auto reinterp = cast(TypedefBaseType!(T)*) value; 115 this.visitAll(reinterp); 116 } 117 else static if (isAggregateType!(T)) 118 { 119 bool ignore = false; 120 121 static if (is(T == class)) 122 { 123 ignore = *value is null; 124 125 // Recurse into super class fields 126 static if (is(T Bases == super)) 127 { 128 foreach (Base; Bases) 129 { 130 static if (Base.init.tupleof.length) 131 { 132 Base base = *value; // implicit upcast 133 this.visitAll(&base); 134 } 135 } 136 } 137 } 138 139 if (!ignore) 140 { 141 foreach (ref field; (*value).tupleof) 142 { 143 this.visitAll(&field); 144 } 145 } 146 } 147 else static if (isArrayType!(T)) 148 { 149 static if ( 150 isArrayType!(T) == ArrayKind.Static 151 || isArrayType!(T) == ArrayKind.Dynamic) 152 { 153 foreach (ref elem; *value) 154 this.visitAll(&elem); 155 } 156 else 157 { 158 static assert (false, "AA currently not supported"); 159 } 160 } 161 else static if (isFunctionType!(T)) 162 { 163 // ignored for now (until someone proposes useful semantics) 164 } 165 else static if (isPointerType!(T)) 166 { 167 if (*value !is null) 168 this.visitAll(*value); 169 } 170 else static if (is(T U == enum)) 171 { 172 auto reinterp = cast(U*) value; 173 this.visitAll(reinterp); 174 } 175 else 176 { 177 static assert ( 178 false, 179 "Unexpected type kind during recursive iteration: " 180 ~ T.stringof 181 ); 182 } 183 } 184 }