1 /** Templates for compile-time introspection.
2 
3     Authors:    Lars Tandle Kyllingstad
4     Copyright:  Copyright (c) 2009–2010, Lars T. Kyllingstad. All rights reserved.
5     License:    Boost License 1.0
6 */
7 module scid.core.traits;
8 
9 
10 import std.complex;
11 import std.traits;
12 
13 
14 
15 
16 /** Detect whether T is a complex floating-point type. */
17 template isComplex(T)
18 {
19     enum bool isComplex = is(T==cfloat) || is(T==cdouble) || is(T==creal)
20         || is(T==Complex!float) || is(T==Complex!double) || is(T==Complex!real);
21 }
22 
23 version(unittest)
24 {
25     static assert (isComplex!cdouble);
26     static assert (isComplex!(Complex!double));
27     static assert (!isComplex!double);
28     static assert (!isComplex!int);
29 }
30 
31 
32 /** Detect whether T is a FORTRAN-compatible floating-point type.
33     The FORTRAN-compatible types are: float, double, cfloat, cdouble.
34 */
35 template isFortranType(T) if (!isComplex!T)
36 {
37     enum bool isFortranType = is(T==float) || is(T==double);
38 }
39 
40 template isFortranType(T) if (isComplex!T)
41 {
42     enum bool isFortranType = isFortranType!(typeof(T.re));
43 }
44 
45 version(unittest)
46 {
47     static assert (isFortranType!double);
48     static assert (isFortranType!cdouble);
49     static assert (isFortranType!(Complex!double));
50     static assert (!isFortranType!real);
51     static assert (!isFortranType!creal);
52     static assert (!isFortranType!(Complex!real));
53 }
54 
55 
56 /** Evaluates to true if T is a callable type, i.e. a function, delegate
57     or functor type.
58 */
59 template isCallable(T)
60 {
61     enum bool isCallable = is (T == return)  ||  isFunctor!(T);
62 }
63 
64 
65 unittest
66 {
67     struct FunctorT
68     {
69         void opCall(int i) { return; }
70     }
71 
72     static assert (isCallable!(void function(int)));
73     static assert (isCallable!(void delegate(int)));
74     static assert (isCallable!(FunctorT));
75 }
76 
77 
78 
79 
80 /** Evaluates to true if T is a functor, i.e. a class or struct with
81     an opCall method.
82 */
83 template isFunctor(T)
84 {
85     enum bool isFunctor = is (typeof(T.opCall) == return);
86 }
87 
88 
89 unittest
90 {
91     struct FunctorT
92     {
93         void opCall(int i) { return; }
94     }
95 
96     static assert (!isFunctor!(void function(int)));
97     static assert (!isFunctor!(void delegate(int)));
98     static assert (isFunctor!(FunctorT));
99 }
100 
101 
102 
103 
104 /** Evaluates to true if the following compiles:
105     ---
106     RetT ret = F.init(ArgT.init);
107     ---
108 */
109 template isUnaryFunction(F, RetT, ArgT)
110 {
111     enum isUnaryFunction = __traits(compiles,
112     {
113         RetT ret = F.init(ArgT.init);
114     });
115 }
116 
117 
118 unittest
119 {
120     real f(real x) { return x*1.0; }
121     static assert (isUnaryFunction!(typeof(&f), real, real));
122     static assert (isUnaryFunction!(typeof(&f), double, int));
123     static assert (!isUnaryFunction!(typeof(&f), int, real));
124     static assert (!isUnaryFunction!(typeof(&f), real, string));
125 }
126 
127 
128 
129 
130 /** Evaluates to true if the following compiles:
131     ---
132     F.init(ArgT.init); // No return type check.
133     ---
134 */
135 template isUnaryFunction(F, ArgT)
136 {
137     enum isUnaryFunction = __traits(compiles,
138     {
139         F.init(ArgT.init);
140     });
141 }
142 
143 
144 unittest
145 {
146     real f(real x) { return x*1.0; }
147     static assert (isUnaryFunction!(typeof(&f), real));
148     static assert (isUnaryFunction!(typeof(&f), double));
149     static assert (isUnaryFunction!(typeof(&f), int));
150     static assert (!isUnaryFunction!(typeof(&f), string));
151 }
152 
153 
154 
155 
156 /** Evaluates to true if FuncType is a vector field, i.e. a callable type
157     that takes an ArgType[] array as input and returns a RetType[] array.
158 */
159 template isVectorField(FuncType, ArgType, RetType = ArgType)
160 {
161     enum bool isVectorField = isBufferVectorField!(FuncType, ArgType, RetType)
162         || __traits(compiles, 
163     {
164         ArgType[] x;
165         FuncType f;
166         RetType[] y = f(x);
167     });
168 }
169 
170 version (unittest)
171 {
172     static assert (isVectorField!(real[] delegate(real[]), real));
173     static assert (isVectorField!(real[] function(real[]), real));
174     static assert (!isVectorField!(real delegate(real[]), real));
175     static assert (!isVectorField!(real[] delegate(real), real));
176     static assert (!isVectorField!(real[] delegate(real[]), int));
177 
178     static assert (isVectorField!(int[] delegate(real[]), real, int));
179     static assert (!isVectorField!(int[] delegate(real[]), int, int));
180     static assert (!isVectorField!(int[] delegate(real[]), real, real));
181 }
182 
183 
184 /** Evaluates to true if FuncType is a vector field which takes an additional
185     (but often optional) buffer of type RetType[] as the second argument.
186 */
187 template isBufferVectorField(FuncType, ArgType, RetType = ArgType)
188 {
189     enum bool isBufferVectorField = __traits(compiles,
190     {
191         ArgType[] x;
192         RetType[] buf;
193         FuncType f;
194         RetType[] y = f(x, buf);
195     });
196 }
197 
198 
199 version (unittest)
200 {
201     static assert (isBufferVectorField!(real[] function(real[], real[]), real));
202     static assert (!isBufferVectorField!(real[] function(real[], real), real));
203     static assert (!isBufferVectorField!(real[] function(real[]), real));
204 }
205 
206 
207 
208 
209 /** Evaluates to true if T is an array type.
210 
211 
212     If the second template parameter is specified it is also checked
213     whether the elements of T are of type U.
214 */
215 template isArray(T)
216 {
217     static if (is (T U : U[]))
218     {
219         enum bool isArray = true;
220     }
221     else
222     {
223         enum bool isArray = false;
224     }
225 }
226 
227 
228 /// ditto
229 template isArray(T, U)
230 {
231     static if (is (T V : V[]))
232     {
233         enum bool isArray = is (V == U);
234     }
235     else
236     {
237         enum bool isArray = false;
238     }
239 }
240 
241 
242 unittest
243 {
244     static assert (isArray!(int[]));
245     static assert (isArray!(int[3]));
246     static assert (isArray!(int[][]));
247 
248     static assert (isArray!(int[], int));
249     static assert (isArray!(int[3], int));
250     static assert (isArray!(int[][], int[]));
251 
252     static assert (!isArray!(int));
253     static assert (!isArray!(int[], long));
254 }
255 
256 
257 
258 
259 /** Evaluates to true if T is a one-dimensional array type.
260 
261     If the second template parameter is specified it is also checked
262     whether the elements of T are of type U.
263 */
264 template is1DArray(T)
265 {
266     static if (is (T V : V[]))
267     {
268         enum bool is1DArray = !isArray!(V);
269     }
270     else
271     {
272         enum bool is1DArray = false;
273     }
274 }
275 
276 
277 /// ditto
278 template is1DArray(T, U)
279 {
280     static if (is (T V : V[]))
281     {
282         enum bool is1DArray = is (V == U);
283     }
284     else
285     {
286         enum bool is1DArray = false;
287     }
288 }
289 
290 
291 unittest
292 {
293     static assert(is1DArray!(double[]));
294     static assert(is1DArray!(double[], double));
295     static assert(!is1DArray!(double[], float));
296     static assert(!is1DArray!(double));
297     static assert(!is1DArray!(double[][]));
298 }
299 
300 
301 
302 
303 /** Evaluates to true if T is a two-dimensional array type.
304 
305     If the second template parameter is specified it is also checked
306     whether the elements of T are of type U.
307 */
308 template is2DArray(T)
309 {
310     static if (is (T V : V[][]))
311     {
312         enum bool is2DArray = !isArray!(V);
313     }
314     else
315     {
316         enum bool is2DArray = false;
317     }
318 }
319 
320 
321 /// ditto
322 template is2DArray(T, U)
323 {
324     static if (is (T V : V[][]))
325     {
326         enum bool is2DArray = is (V == U);
327     }
328     else
329     {
330         enum bool is2DArray = false;
331     }
332 }
333 
334 
335 unittest
336 {
337     static assert(is2DArray!(double[][]));
338     static assert(is2DArray!(double[][], double));
339     static assert(!is2DArray!(double[][], float));
340     static assert(!is2DArray!(double[]));
341     static assert(!is2DArray!(double[][][]));
342 }
343 
344 
345 
346 
347 
348 /** Evaluates to a type tuple of the argument types of T, where
349     T must be either a function, delegate or functor type.
350 */
351 template ArgumentTypeTuple(T)
352 {
353     static if (is (T A == function))
354     {
355         alias A ArgumentTypeTuple;
356     }
357     else static if (is (T A == delegate))
358     {
359         alias ArgumentTypeTuple!(A) ArgumentTypeTuple;
360     }
361     else static if (is (T A == A*))
362     {
363         alias ArgumentTypeTuple!(A) ArgumentTypeTuple;
364     }
365     else static if (isFunctor!(T))
366     {
367         alias ArgumentTypeTuple!(typeof(T.opCall)) ArgumentTypeTuple;
368     }
369     else
370     {
371         static assert (false,
372             "ArgumentTypeTuple: Not a callable type: "~T.stringof);
373     }
374 }
375 
376 
377 private template UTTuple(T...) { alias T UTTuple; }
378 unittest
379 {
380     struct FunctorT
381     {
382         real opCall(int i, bool b) { return 0.0; }
383     }
384 
385     static assert (is (ArgumentTypeTuple!(real function(int, bool)) == UTTuple!(int, bool)));
386     static assert (is (ArgumentTypeTuple!(real delegate(int, bool)) == UTTuple!(int, bool)));
387     static assert (is (ArgumentTypeTuple!(FunctorT) == UTTuple!(int, bool)));
388 }
389 
390 
391 
392 
393 /** Evaluates to the inner element type of the array, vector or matrix
394     type T. If T is none of these, BaseElementType evaluates to T.
395 */
396 template BaseElementType(T)
397 {
398     static if (is (T E : E[]))
399     {
400         alias BaseElementType!(E) BaseElementType;
401     }
402     else static if (is (T E : E*))
403     {
404         alias BaseElementType!E BaseElementType;
405     }
406     else static if (is (typeof(&T.opIndex) E == return))
407     {
408         alias BaseElementType!E BaseElementType;
409     }
410     else
411     {
412         alias T BaseElementType;
413     }
414 }
415 
416 
417 unittest
418 {
419     struct VectorS
420     {
421         size_t length;
422         int opIndex(size_t i) { return 0; }
423     }
424     struct MatrixS
425     {
426         size_t rows,cols;
427         int opIndex(size_t i, size_t j) { return 0; }
428     }
429 
430     static assert (is(BaseElementType!(int) == int));
431     static assert (is(BaseElementType!(int[]) == int));
432     static assert (is(BaseElementType!(int[][]) == int));
433 
434     static assert (is(BaseElementType!(int*) == int));
435     static assert (is(BaseElementType!(int**) == int));
436     static assert (is(BaseElementType!(int[]*) == int));
437 
438     static assert (is(BaseElementType!(VectorS) == int));
439     static assert (is(BaseElementType!(MatrixS) == int));
440 }
441 
442 
443 
444 
445 /** Checks whether all the types U... are implicitly
446     convertible to T.
447 */
448 template allConvertibleTo(T, U...) if (U.length > 0)
449 {
450     static if (U.length == 1)
451         enum allConvertibleTo = is(U[0] : T);
452     else
453         enum allConvertibleTo = is(U[0] : T) && allConvertibleTo!(T, U[1 .. $]);
454 }
455 
456 
457 unittest
458 {
459     static assert (allConvertibleTo!(dchar, char, wchar, dchar));
460     static assert (!allConvertibleTo!(wchar, char, wchar, dchar));
461 }