1 /** Additions to $(STDMODULE _traits).
2 
3 Copyright: Denis Shelomovskij 2011-2013
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Denis Shelomovskij
8 */
9 module unstd.traits;
10 
11 
12 public import std.traits;
13 
14 import unstd.generictuple;
15 import std.typecons: tuple;
16 
17 
18 // https://github.com/D-Programming-Language/phobos/pull/776
19 /**
20 Returns the element type of an array.
21 
22 For ranges, see also
23 $(STDREF range, ElementEncodingType).
24 */
25 alias ArrayElementType(T : T[]) = T;
26 
27 ///
28 unittest
29 {
30 	static assert( is(ArrayElementType!(int[]) == int));
31 	static assert(is(ArrayElementType!(int[7][8]) == int[7]));
32 	static assert(is(ArrayElementType!string == immutable(char)));
33 }
34 
35 unittest
36 {
37 	static assert( is(ArrayElementType!(long[0]) == long));
38 	static assert(is(ArrayElementType!(int[0][]) == int[0]));
39 	static assert(is(ArrayElementType!(int[][0]) == int[]));
40 
41 	static assert(!is(ArrayElementType!int));
42 }
43 
44 
45 /**
46 Gets the rank (number of dimensions) of a static array type.
47 
48 If $(D T) isn't a static array assumes it to be a $(I zero-dimensional)
49 static array with single element and returns zero.
50 */
51 template staticArrayDims(T)
52 {
53 	static if(isStaticArray!T)
54 		enum staticArrayDims = 1 + staticArrayDims!(ArrayElementType!T);
55 	else
56 		enum staticArrayDims = 0;
57 }
58 
59 ///
60 unittest
61 {
62 	static assert(staticArrayDims!int == 0);
63 	static assert(staticArrayDims!(int[]) == 0);
64 	static assert(staticArrayDims!(int[0]) == 1);
65 	static assert(staticArrayDims!(int[7][8]) == 2);
66 	static assert(staticArrayDims!(int[0][]) == 0);
67 	static assert(staticArrayDims!(int[][0]) == 1);
68 }
69 
70 unittest
71 {
72 	static assert(staticArrayDims!string == 0);
73 	static assert(staticArrayDims!(int[0][0]) == 2);
74 }
75 
76 
77 /**
78 Gets the element type of the innermost array in a multidimensional static array type. 
79 Considers $(D T) to be an $(D n)-dimensional static array type.
80 
81 If $(D T) isn't a static array assumes it to be a $(I zero-dimensional)
82 static array with single element and returns $(D T).
83 */
84 template MultidimStaticArrayElementType(T, size_t n = staticArrayDims!T)
85 {
86 	static assert(staticArrayDims!T >= n, "Not enough static array dimensions");
87 	static if(n)
88 		alias MultidimStaticArrayElementType = MultidimStaticArrayElementType!(ArrayElementType!T, n-1);
89 	else
90 		alias MultidimStaticArrayElementType = T;
91 }
92 
93 ///
94 unittest
95 {
96 	static assert(is(MultidimStaticArrayElementType!int == int));
97 	static assert(is(MultidimStaticArrayElementType!(int[]) == int[]));
98 	static assert(is(MultidimStaticArrayElementType!(int[0]) == int));
99 	static assert(!__traits(compiles, MultidimStaticArrayElementType!(int[7][8], 3)));
100 	static assert(is(MultidimStaticArrayElementType!(int[7][8]) == int));
101 	static assert(is(MultidimStaticArrayElementType!(int[7][8], 1) == int[7]));
102 	static assert(is(MultidimStaticArrayElementType!(int[7][8], 0) == int[7][8]));
103 	static assert(is(MultidimStaticArrayElementType!(int[0][]) == int[0][]));
104 	static assert(is(MultidimStaticArrayElementType!(int[][0]) == int[]));
105 }
106 
107 unittest
108 {
109 	static assert(is(MultidimStaticArrayElementType!string == string));
110 }
111 
112 
113 /**
114 Calculates the total element count of a multidimensional static array.
115 Considers $(D T) to be an $(D n)-dimensional static array type.
116 
117 If $(D T) isn't a static array assumes it to be a $(I zero-dimensional)
118 static array with single element and returns 1.
119 */
120 template multidimStaticArrayElementCount(T, size_t n = staticArrayDims!T)
121 {
122 	static assert(staticArrayDims!T >= n, "Not enough static array dimensions");
123 	enum multidimStaticArrayElementCount = T.sizeof / MultidimStaticArrayElementType!(T, n).sizeof;
124 }
125 
126 ///
127 unittest
128 {
129 	static assert(multidimStaticArrayElementCount!int == 1);
130 	static assert(multidimStaticArrayElementCount!(int[]) == 1);
131 	static assert(multidimStaticArrayElementCount!(int[0]) == 0);
132 	static assert(!__traits(compiles, multidimStaticArrayElementCount!(int[7][8], 3)));
133 	static assert(multidimStaticArrayElementCount!(int[7][8]) == 7 * 8);
134 	static assert(multidimStaticArrayElementCount!(int[7][8], 1) == 8);
135 	static assert(multidimStaticArrayElementCount!(int[7][8], 0) == 1);
136 	static assert(multidimStaticArrayElementCount!(int[0][]) == 1);
137 	static assert(multidimStaticArrayElementCount!(int[][0]) == 0);
138 }
139 
140 unittest
141 {
142 	static assert(multidimStaticArrayElementCount!string == 1);
143 }
144 
145 
146 /**
147 Get, as an expression tuple, multidimensional static array lengths considering
148 $(D T) to be $(D n)-dimensioanl static array.
149 */
150 template multidimStaticArrayLengths(T, size_t n = staticArrayDims!T)
151 {
152 	static assert(staticArrayDims!T >= n, "Not enough static array dimensions");
153 
154 	static if(n)
155 		alias multidimStaticArrayLengths = expressionTuple!(T.length, multidimStaticArrayLengths!(ArrayElementType!T, n-1));
156 	else
157 		alias multidimStaticArrayLengths = expressionTuple!();
158 }
159 
160 ///
161 unittest
162 {
163 	alias e1 = multidimStaticArrayLengths!(int[7][8]);
164 	static assert(e1.length == 2 && e1[0] == 8 && e1[1] == 7);
165 
166 	alias e2 = multidimStaticArrayLengths!(int[7][8], 1);
167 	static assert(e2.length == 1 && e2[0] == 8);
168 	static assert(multidimStaticArrayLengths!(int[7][8], 0).length == 0);
169 }
170 
171 unittest
172 {
173 	static assert(multidimStaticArrayLengths!int.length == 0);
174 	static assert(multidimStaticArrayLengths!(int[]).length == 0);
175 	static assert(multidimStaticArrayLengths!string.length == 0);
176 	static assert(multidimStaticArrayLengths!(int[0]) == expressionTuple!(0));
177 	static assert(!__traits(compiles, multidimStaticArrayLengths!(int[7][8], 3)));
178 	static assert(multidimStaticArrayLengths!(int[7][8]) == expressionTuple!(8, 7));
179 	static assert(multidimStaticArrayLengths!(int[7][8], 1) == expressionTuple!(8));
180 	static assert(multidimStaticArrayLengths!(int[7][8], 0).length == 0);
181 	static assert(multidimStaticArrayLengths!(int[0][]).length == 0);
182 	static assert(multidimStaticArrayLengths!(int[][0]) == expressionTuple!(0));
183 }
184 
185 
186 /// Detect whether tuple $(D A) is $(D PackedGenericTuple).
187 enum isPackedTuple(alias A) = __traits(compiles, A.Tuple);
188 
189 /// ditto
190 enum isPackedTuple(A) = false;
191 
192 
193 /**
194 Get all types $(D T) include except $(D Extracted) without duplicates
195 in such order that every compound type precedes types it includes.
196 */
197 template ExtractTypes(T, Extracted...) if(isTypeTuple!Extracted)
198 {
199 	static if(staticIndexOf!(T, Extracted) != -1)
200 	{
201 		alias ExtractTypes = TypeTuple!();
202 	}
203 	else
204 	{
205 		template Extract(U)
206 		{
207 			alias Extract = .ExtractTypes!(U, Extracted, T);
208 		}
209 
210 		static if(is(PointerTarget!T PT))
211 		{
212 			alias ExtractTypes = TypeTuple!(T, Extract!PT);
213 		}
214 		else static if(__traits(isScalar, T))
215 		{
216 			alias ExtractTypes = TypeTuple!T;
217 		}
218 		else static if(is(T == struct) || is(T == class) || is(T == union))
219 		{
220 			alias ExtractTypes = TypeTuple!(T, NoDuplicates!(MapTuple!(Extract, FieldTypeTuple!T)));
221 		}
222 		else static if(isArray!T)
223 		{
224 			alias ExtractTypes = TypeTuple!(T, Extract!(ArrayElementType!T));
225 		}
226 		else
227 			static assert(0);
228 	}
229 }
230 
231 ///
232 unittest
233 {
234 	static assert(is(ExtractTypes!int == TypeTuple!int));
235 	static assert(is(ExtractTypes!(int*) == TypeTuple!(int*, int)));
236 	static assert(is(ExtractTypes!(int*, int) == TypeTuple!(int*)));
237 
238 	static struct S1 { int i; real r; }
239 	static assert(is(ExtractTypes!S1 == TypeTuple!(S1, int, real)));
240 	static assert(is(ExtractTypes!(S1, int) == TypeTuple!(S1, real)));
241 
242 	static struct S2
243 	{
244 		int* iptr;
245 		S1* s1ptr1, s1ptr2;
246 		S2[] s2darr;
247 		S2[3]* s2sarr;
248 	}
249 	static assert(is(ExtractTypes!S2 == TypeTuple!(
250 		S2,                // for `S2` itself
251 		int*, int,         // for `int*`
252 		S1*, S1, real,     // for `S1*`
253 		S2[],              // for `S2[]`
254 		S2[3]*, S2[3]      // for `S2[3]*`
255 	)));
256 }
257 
258 unittest
259 {
260 	static assert(!__traits(compiles, ExtractTypes!(int*, 0)));
261 
262 	static struct S1 { int i; real r; }
263 
264 	static class C { real n; }
265 	static assert(is(ExtractTypes!C == TypeTuple!(C, real)));
266 
267 	static struct S3 { C c; S1* s1ptr1, s1ptr2; C* cptr; }
268 	static assert(is(ExtractTypes!S3 == TypeTuple!(S3, C, real, S1*, S1, int, C*)));
269 }
270 
271 
272 /**
273 true iff $(D T) is a type. Usable for analysing generic tuples.
274 */
275 enum isType(T) = true;
276 
277 /// ditto
278 enum isType(alias T) = false;
279 
280 ///
281 unittest
282 {
283 	static assert(isType!int && isType!string);
284 	static assert(!isType!0 && !isType!"str");
285 	static assert(!isType!isType);
286 }
287 
288 unittest
289 {
290 	static assert(isType!(int[]));
291 	static assert(isType!(TypeTuple!string));
292 	static assert(!__traits(compiles, isType!()));
293 	static assert(!__traits(compiles, isType!(int, string)));
294 
295 	static assert(!isType!'a');
296 
297 	static @property void __vp() { }
298 	static @property int __ip() { return 0; }
299 	static assert(!isType!(__vp));
300 	static assert(!isType!(__ip));
301 
302 	static void __vf() { }
303 	static int __if() { return 0; }
304 	//static assert(!isType!(__vf())); //FIXME
305 	static assert(!isType!(__if()));
306 }