1 /*******************************************************************************
2 3 Value comparison for structs and arbitrary types.
4 5 Does a deep equality comparison of one type to another.
6 7 'Deep' meaning:
8 * The _contents_ of dynamic arrays are compared
9 * Types are recursed, allowing multi-dimensional arrays to be compared
10 * All members of structs are compared (recursively, if needed).
11 12 Copyright:
13 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
14 All rights reserved.
15 16 License:
17 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
18 Alternatively, this file may be distributed under the terms of the Tango
19 3-Clause BSD License (see LICENSE_BSD.txt for details).
20 21 *******************************************************************************/22 23 moduleocean.core.DeepCompare;
24 25 26 27 importocean.meta.types.Qualifiers;
28 29 version (unittest) importocean.core.Test;
30 31 /***************************************************************************
32 33 Given a type T, returns true if the type contains a struct or
34 a floating-point type.
35 36 ***************************************************************************/37 38 privatetemplateneedsSpecialCompare(T)
39 {
40 staticif ( is(TV : V[]) )
41 {
42 // T is an array. Strip off all of the [] to get43 // the ultimate element type.44 45 enum46 {
47 needsSpecialCompare = needsSpecialCompare!(V)
48 };
49 }
50 else51 {
52 // It's special if it has NaN, or is a struct53 enum54 {
55 needsSpecialCompare = is ( typeof (T.nan) ) ||
56 is ( T == struct )
57 };
58 }
59 }
60 61 /***************************************************************************
62 63 Compares two values and returns true if they are equal.
64 65 This differs from built-in == in two respects.
66 67 1. Dynamic arrays are compared by value, even when they are struct members.
68 69 2. Floating point numbers which are NaN are considered equal.
70 This preserves the important property that deepEquals(x, x) is true
71 for all x.
72 73 Classes are compared in the normal way, using opEquals.
74 75 Params:
76 a = Struct to be compared with b
77 b = Struct to be compared with a
78 79 Returns:
80 true if equal
81 82 ***************************************************************************/83 84 85 publicbooldeepEquals(T)(Ta, Tb)
86 {
87 staticif ( is (typeof(T.nan)) )
88 {
89 //pragma(msg, "Comparing float: " ~ T.stringof);90 91 // Deal with NaN.92 // If x is NaN, then x == x is false93 // So we return true if both a and b are NaN.94 // (In D2, we'd just use "return a is b").95 96 returna == b || ( a != a && b != b);
97 }
98 elsestaticif ( is ( T == struct) )
99 {
100 //pragma(msg, "Comparing struct: " ~ T.stringof);101 102 foreach(i, U; typeof(a.tupleof) )
103 {
104 staticif ( needsSpecialCompare!(U) )
105 {
106 //pragma(msg, "Comparing special element " ~ U.stringof );107 108 // It is a special type (struct or float),109 // or an array of special types110 111 if ( !deepEquals(a.tupleof[i], b.tupleof[i]) )
112 {
113 returnfalse;
114 }
115 }
116 else117 {
118 //pragma(msg, "\t not a special case: " ~ typeof(a.tupleof[i]).stringof);119 120 if ( a.tupleof[i] != b.tupleof[i] )
121 {
122 returnfalse;
123 }
124 }
125 }
126 returntrue;
127 }
128 elsestaticif ( is(TV : V[]) )
129 {
130 // T is an array.131 // If it is one of the special cases, we need to132 // do an element-by-element compare.133 134 staticif ( needsSpecialCompare!(V) )
135 {
136 //pragma(msg, "Comparing element-by-element of array " ~ T.stringof);137 138 // Compare element-by-element.139 140 if (a.length != b.length)
141 {
142 returnfalse;
143 }
144 145 foreach ( j, m; a )
146 {
147 if ( !deepEquals(m, b[j]) )
148 returnfalse;
149 }
150 151 returntrue;
152 }
153 else154 {
155 //pragma(msg, "Simple array compare " ~ T.stringof);156 157 // Not a special case, we can just use the builtin ==.158 // Note that this works even for the multidimensional case.159 160 returna == b;
161 }
162 }
163 else164 {
165 //pragma(msg, "\t not a special case" ~ T.stringof);166 returna == b;
167 }
168 }
169 170 171 unittest172 {
173 structS0174 {
175 }
176 177 structS1178 {
179 int [] x;
180 }
181 182 structS2183 {
184 S1 [] y;
185 }
186 187 structS3188 {
189 S1 [][] z;
190 }
191 192 structS4193 {
194 doublex;
195 }
196 197 structS5198 {
199 double[] x;
200 }
201 202 S0a, b;
203 test(deepEquals(a, b));
204 S1a1, b1;
205 a1.x = [ 1, 2, 3];
206 b1.x = [ 1, 2, 3];
207 test(a1 !isb1);
208 test(deepEquals(a1, b1));
209 test(a1.x == b1.x);
210 S2a2, b2;
211 a2.y = [a1];
212 b2.y = [b1];
213 test(deepEquals(a2, b2));
214 S3a3, b3;
215 a3.z = [a2.y];
216 b3.z = [b2.y];
217 test(deepEquals(a3, b3));
218 b3.z = [null];
219 test(!deepEquals(a3, b3));
220 S4a4;
221 a4.x = double.nan;
222 test(deepEquals(a4, a4));
223 S5a5;
224 a5.x ~= double.nan;
225 test(deepEquals(a5, a5));
226 }