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 }