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 }