1 /** Various extensions to std.traits. 2 3 Copyright: Per Nordlöw 2018-. 4 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 Authors: $(WEB Per Nordlöw) 6 7 See_Also: http://forum.dlang.org/thread/jbyixfbuefvdlttnyclu@forum.dlang.org#post-mailman.2199.1353742037.5162.digitalmars-d-learn:40puremagic.com 8 See_Also: http://forum.dlang.org/post/rjrdgmijsmvvsbpinidz@forum.dlang.org 9 */ 10 module nxt.traits_ex; 11 12 import std.traits: isArray, ParameterTypeTuple, isStaticArray, isDynamicArray, isSomeChar, isSomeString, isExpressions, isIntegral, isSigned, isUnsigned, isAssignable, isIterable; 13 import std.meta : allSatisfy; 14 import std.range: ElementType, isForwardRange, isRandomAccessRange, isInputRange, isBidirectionalRange, isOutputRange; 15 16 /** Returns: `true` iff $(D ptr) is handled by D's garbage collector (GC). 17 */ 18 bool isGCPointer(T)(const T* ptr) 19 @trusted nothrow @nogc 20 { 21 import core.memory : GC; 22 return cast(bool)GC.addrOf(ptr); 23 } 24 25 /// 26 @system nothrow unittest 27 { 28 int s; 29 int* sp = &s; 30 assert(!sp.isGCPointer); 31 int* ip = new int; 32 assert(ip.isGCPointer); 33 } 34 35 /** Returns: `true` iff all values `V` are the same. 36 * 37 * See_Also: https://forum.dlang.org/post/lnsreapgttmdeuscsupp@forum.dlang.org 38 */ 39 template allSameIterative(V...) 40 { 41 static if (V.length <= 1) 42 { 43 enum allSameIterative = true; 44 } 45 else 46 { 47 static foreach (Vi; V[1 .. $]) 48 { 49 static if (is(typeof(allSameIterative) == void) && // not yet defined 50 !isSame!(V[0], Vi)) 51 { 52 enum allSameIterative = false; 53 } 54 } 55 static if (is(typeof(allSameIterative) == void)) // if not yet defined 56 { 57 enum allSameIterative = true; 58 } 59 } 60 } 61 62 /// 63 @safe pure nothrow @nogc unittest 64 { 65 static assert( allSameIterative!()); 66 static assert( allSameIterative!(42)); 67 static assert( allSameIterative!(42, 42)); 68 static assert( allSameIterative!(42, 42, 42)); 69 static assert(!allSameIterative!(42, 43, 42)); 70 71 static assert( allSameIterative!(int)); 72 static assert( allSameIterative!(int, int)); 73 static assert( allSameIterative!(int, int, int)); 74 static assert(!allSameIterative!(int, byte, int)); 75 } 76 77 alias allSame = allSameIterative; // default to iterative variant for now 78 alias isHomogeneousType = allSame; 79 enum isHomogeneousTuple(T) = isHomogeneousType!(T.Types); 80 81 /// 82 @safe pure nothrow @nogc unittest 83 { 84 static assert(isHomogeneousTuple!(Tuple!(int, int, int))); 85 static assert(isHomogeneousTuple!(Tuple!(float, float, float))); 86 static assert(!isHomogeneousTuple!(Tuple!(int, float, double))); 87 } 88 89 enum isHomogeneousTupleOf(T, E) = (isHomogeneousType!(T) && 90 is(T.Types[0] == E)); 91 92 /// 93 @safe pure nothrow @nogc unittest 94 { 95 static assert(isHomogeneousTupleOf!(Tuple!(int, int, int), int)); 96 static assert(isHomogeneousTupleOf!(Tuple!(float, float, float), float)); 97 static assert(!isHomogeneousTupleOf!(Tuple!(float, float, float), int)); 98 } 99 100 /** 101 Returns $(D true) if at least one type in the $(D Tuple T) 102 is not the same as the others. 103 */ 104 enum isHeterogeneous(T) = !isHomogeneousType!T; 105 106 template allSameTypeIterative(V...) 107 // TODO restrict `V` to types only 108 { 109 static if (V.length >= 2) 110 { 111 static foreach (Vi; V[1 .. $]) 112 { 113 static if (is(typeof(allSameTypeIterative) == void) && // not yet defined 114 !is(V[0] == Vi)) // 10% faster than `!isSame(V[0], Vi)` 115 { 116 enum allSameTypeIterative = false; 117 } 118 } 119 } 120 static if (is(typeof(allSameTypeIterative) == void)) // if not yet defined 121 { 122 enum allSameTypeIterative = true; 123 } 124 } 125 alias allSameType = allSameTypeIterative; 126 127 /// 128 @safe pure nothrow @nogc unittest 129 { 130 static assert( allSameTypeIterative!(int)); 131 static assert( allSameTypeIterative!(int, int)); 132 133 static assert( allSameTypeIterative!(int, int, int)); 134 static assert(!allSameTypeIterative!(int, byte, int)); 135 136 static assert( allSameTypeIterative!(int, int, int, int)); 137 static assert(!allSameTypeIterative!(int, byte, int, byte)); 138 139 static assert(!allSameTypeIterative!(int, const(int))); 140 static assert(!allSameTypeIterative!(byte, const(int))); 141 } 142 143 /** Returns: `true` iff all values `V` are the same. 144 145 Same as NoDuplicates!V.length == 1 146 147 See_Also: https://forum.dlang.org/post/ptnzlhnkuetijhgrgumd@forum.dlang.org 148 See_Also: http://forum.dlang.org/post/iflpslqgrixdjwrlqqvn@forum.dlang.org 149 See_Also: http://forum.dlang.org/post/mheumktihihfsxxxapff@forum.dlang.org 150 */ 151 template allSameRecursive(V...) 152 if (isExpressions!V) 153 { 154 static if (V.length <= 1) 155 { 156 enum allSameRecursive = true; 157 } 158 else static if (V.length & 1) // odd count 159 { 160 enum allSameRecursive = (V[0] == V[$ - 1] && // first equals last 161 V[0 .. $/2] == V[$/2 .. $-1] && // (first half) equals (second half minus last element) 162 allSameRecursive!(V[0 .. $/2])); 163 } 164 else // event count 165 { 166 enum allSameRecursive = (V[0 .. $/2] == V[$/2 .. $] && // (first half) equals (second half) 167 allSameRecursive!(V[0 .. $/2])); 168 } 169 } 170 171 /// 172 @safe pure nothrow @nogc unittest 173 { 174 static assert( allSameRecursive!()); 175 static assert( allSameRecursive!(42)); 176 static assert( allSameRecursive!(42, 42)); 177 static assert( allSameRecursive!(42, 42, 42)); 178 static assert(!allSameRecursive!(42, 43, 42)); 179 } 180 181 template allSameTypeHybrid(V...) 182 // TODO restrict `V` to types only 183 { 184 static if (V.length >= 8) 185 { 186 static if (V.length <= 1) 187 { 188 enum allSameTypeHybrid = true; 189 } 190 else static if (V.length == 2) 191 { 192 enum allSameTypeHybrid = is(V[0] == V[1]); 193 } 194 static if (V.length & 1) // odd count 195 { 196 enum allSameTypeHybrid = (is(V[0] == V[$ - 1]) && // first equals last 197 is(V[0 .. $/2] == V[$/2 .. $-1]) && // (first half) equals (second half minus last element) 198 allSameTypeHybrid!(V[0 .. $/2])); 199 } 200 else // even count 201 { 202 enum allSameTypeHybrid = (is(V[0 .. $/2] == V[$/2 .. $]) && // (first half) equals (second half) 203 allSameTypeHybrid!(V[0 .. $/2])); 204 } 205 } 206 else 207 { 208 enum allSameTypeHybrid = allSameTypeIterative!(V); 209 } 210 } 211 212 /// 213 @safe pure nothrow @nogc unittest 214 { 215 static assert(allSameTypeHybrid!()); 216 static assert(allSameTypeHybrid!(int)); 217 static assert(allSameTypeHybrid!(int, int)); 218 static assert(!allSameTypeHybrid!(int, double)); 219 static assert(!allSameTypeHybrid!(int, int, double)); 220 static assert(allSameTypeHybrid!(Tuple!(int, int, int).Types, int)); 221 222 static assert(!allSameTypeHybrid!(int, const(int))); 223 static assert(!allSameTypeHybrid!(byte, const(int))); 224 } 225 226 /** Variant of `allSameTypeRecursive`. 227 */ 228 template allSameTypeRecursive2(V...) 229 if (isExpressions!(V)) 230 { 231 static if (V.length <= 1) 232 { 233 enum allSameTypeRecursive2 = true; 234 } 235 else 236 { 237 enum allSameTypeRecursive2 = (V[0] == V[1] && 238 allSameTypeRecursive2!(V[1..$])); 239 } 240 } 241 242 /// 243 @safe pure nothrow @nogc unittest 244 { 245 static assert(allSameTypeRecursive2!()); 246 static assert(allSameTypeRecursive2!(42)); 247 static assert(!allSameTypeRecursive2!(41, 42)); 248 static assert(allSameTypeRecursive2!(42, 42, 42)); 249 } 250 251 /** Returns: `true` iff all types `T` are the same. 252 */ 253 template allSameTypeRecursive(V...) 254 // TODO restrict `V` to types only 255 { 256 static if (V.length <= 1) 257 { 258 enum allSameTypeRecursive = true; 259 } 260 else static if (V.length & 1) // odd count 261 { 262 enum allSameTypeRecursive = (is(V[0] == V[$ - 1]) && // first equals last 263 is(V[0 .. $/2] == V[$/2 .. $-1]) && // (first half) equals (second half minus last element) 264 allSameTypeRecursive!(V[0 .. $/2])); 265 } 266 else // even count 267 { 268 enum allSameTypeRecursive = (is(V[0 .. $/2] == V[$/2 .. $]) && // (first half) equals (second half) 269 allSameTypeRecursive!(V[0 .. $/2])); 270 } 271 } 272 273 /// 274 @safe pure nothrow @nogc unittest 275 { 276 static assert(allSameTypeRecursive!()); 277 static assert(allSameTypeRecursive!(int)); 278 static assert(allSameTypeRecursive!(int, int)); 279 static assert(!allSameTypeRecursive!(int, double)); 280 static assert(!allSameTypeRecursive!(int, int, double)); 281 static assert(allSameTypeRecursive!(Tuple!(int, int, int).Types, int)); 282 283 static assert(!allSameTypeRecursive!(int, const(int))); 284 static assert(!allSameTypeRecursive!(byte, const(int))); 285 } 286 287 /** Returns: `true` iff all types `T` are the same. */ 288 enum allSameType_alternative(T...) = (!T.length || 289 (is(T[0] == T[T.length > 1]) && 290 allSameType1!(T[1 .. $]))); 291 292 293 import std.typecons : isTuple; 294 295 /** 296 Returns $(D true) if all types in the $(D Tuple T) are the same. 297 TODO: Remove when this is merged: https://github.com/D-Programming-Language/phobos/pull/3395 298 See_Also: https://github.com/D-Programming-Language/phobos/pull/1672/files 299 */ 300 template allSameTypesInTuple(T) 301 if (isTuple!T) 302 { 303 alias types = T.Types; 304 static if (types.length > 0) 305 { 306 template isSameTypeAsHead(U) 307 { 308 enum isSameTypeAsHead = is(U == types[0]); 309 } 310 import std.meta : allSatisfy; 311 enum allSameTypesInTuple = allSatisfy!(isSameTypeAsHead, types); 312 } 313 else 314 enum allSameTypesInTuple = true; 315 } 316 317 /// 318 @safe pure nothrow unittest 319 { 320 alias HOTUP = Tuple!(int, int, int); 321 static assert(allSameTypesInTuple!HOTUP); 322 323 const HOTUP hotup = HOTUP(1, 2, 3); 324 static assert(allSameTypesInTuple!(typeof(hotup))); 325 326 alias HETUP = Tuple!(string, bool, float); 327 static assert(!allSameTypesInTuple!(HETUP)); 328 329 const HETUP hetup = HETUP("test", false, 2.345); 330 static assert(!allSameTypesInTuple!(typeof(hetup))); 331 332 alias ZTUP = Tuple!(); 333 static assert(allSameTypesInTuple!ZTUP); 334 335 const ZTUP ztup = ZTUP(); 336 static assert(allSameTypesInTuple!(typeof(ztup))); 337 } 338 339 /** Returns: tuple `tup` as a dynamic array. 340 */ 341 auto asDynamicArray(T)(inout T tup) 342 if (allSameTypeRecursive!(T.Types)) 343 { 344 alias E = T.Types[0]; 345 E[] a = new E[T.length]; 346 a.length = T.length; 347 foreach (const i, e; tup) 348 { 349 a[i] = e; 350 } 351 return a; 352 } 353 354 /// 355 pure nothrow unittest 356 { 357 import std.typecons: tuple; 358 auto tup = tuple("a", "b", "c", "d"); 359 string[4] arr = ["a", "b", "c", "d"]; 360 assert(tup.asDynamicArray() == arr); 361 } 362 363 /** Is `true` if `R` is iterable over references to its elements. 364 * 365 * Typically used to iterate over ranges with uncopyable elements. 366 * 367 * TODO Add to Phobos. 368 */ 369 enum bool isRefIterable(T) = is(typeof({ foreach (ref elem; T.init) {} })); 370 371 /// Useful aliases for combinations of range predicates. 372 enum isIterableOf(R, E) = isIterable!R && is(ElementType!R == E); 373 enum isIterableOfUnqual(R, E) = isIterable!R && is(Unqual!(ElementType!R) == Unqual!E); 374 enum isIterableOfSomeString(R) = (isIterable!R && isSomeString!(ElementType!R)); 375 376 /// 377 @safe pure nothrow @nogc unittest 378 { 379 alias E = string; 380 alias I = int; 381 alias R = typeof(["a", "b"]); 382 static assert(isIterableOf!(R, E)); 383 static assert(isIterableOfUnqual!(R, const(E))); 384 static assert(isIterableOfSomeString!(R)); 385 static assert(!isIterableOf!(R, I)); 386 } 387 388 // TODO use this instead? 389 version(none) private template isInputRangeOf(R, E) 390 { 391 import std.range.primitives: isInputRange, ElementType; 392 import std.traits: Unqual; 393 enum isInputRangeOf = isInputRange!R && is(Unqual!(ElementType!R) == E); 394 } 395 396 /// Useful aliases for combinations of range predicates. 397 enum isRandomAccessRangeOf(R, E) = isRandomAccessRange!R && is(ElementType!R == E); 398 enum isForwardRangeOf(R, E) = isForwardRange!R && is(ElementType!R == E); 399 enum isInputRangeOf(R, E) = isInputRange!R && is(ElementType!R == E); 400 401 enum isInputRangeOfUnqual(R, E) = (isInputRange!R && 402 is(Unqual!(ElementType!R) == E)); 403 404 enum isBidirectionalRangeOf(R, E) = isBidirectionalRange!R && is(ElementType!R == E); 405 enum isOutputRangeOf(R, E) = isOutputRange!R && is(ElementType!R == E); 406 enum isArrayOf(R, E) = isArray!R && is(ElementType!R == E); 407 enum isArrayOfSomeString(R) = isArray!R && isSomeString!(ElementType!R); 408 409 enum isSourceAssignableTo(R, E) = (isInputRange!R && 410 isAssignable!(E, ElementType!R)); 411 412 /// 413 @safe pure unittest 414 { 415 static assert(isSomeString!(string)); 416 static assert(isSomeString!(const string)); 417 418 static assert(isSomeString!(const(char)[])); 419 static assert(isSomeString!(const char[])); 420 } 421 422 @safe pure nothrow @nogc unittest 423 { 424 alias R = typeof(["a", "b"]); 425 static assert(isArrayOf!(R, string)); 426 static assert(isArrayOfSomeString!(R)); 427 } 428 429 enum isSource(R) = isInputRange!(R); 430 enum isRange(R) = isInputRange!(R); 431 432 enum isSourceOf(R, E) = isInputRangeOf!(R, E); 433 enum isSourceOfUnqual(R, E) = isInputRangeOfUnqual!(R, E); 434 enum isSink(R) = isOutputRange!(R); 435 enum isSinkOf(R, E) = isOutputRangeOf!(R, E); 436 437 enum isSourceOfSomeChar(R) = (isSource!R && 438 isSomeChar!(ElementType!R)); 439 alias isSomeLazyString = isSourceOfSomeChar; 440 441 @safe pure nothrow @nogc unittest 442 { 443 import std.meta : AliasSeq; 444 foreach (Ch; AliasSeq!(char, wchar, dchar)) 445 { 446 assert(isSourceOfSomeChar!(Ch[])); 447 assert(isSourceOfSomeChar!(const(Ch)[])); 448 assert(isSourceOfSomeChar!(immutable(Ch)[])); 449 } 450 } 451 452 enum isSourceOfSomeString(R) = (isSource!R && isSomeString!(ElementType!R)); 453 alias isSomeStringSource = isSourceOfSomeString; 454 455 import std.functional: unaryFun, binaryFun; 456 457 /* TODO Do we need use of unaryFun and binaryFun here? */ 458 alias isEven = unaryFun!(a => (a & 1) == 0); // Limit to Integers? 459 alias isOdd = unaryFun!(a => (a & 1) == 1); // Limit to Integers? 460 alias lessThan = binaryFun!((a, b) => a < b); 461 alias greaterThan = binaryFun!((a, b) => a > b); 462 463 /** Check if `T` has an even length. */ 464 enum hasEvenLength(T...) = !(T.length & 1); 465 @safe pure nothrow @nogc unittest 466 { 467 static assert(!hasEvenLength!(1)); 468 static assert(hasEvenLength!(1, 2)); 469 static assert(!hasEvenLength!(1, 2, 3)); 470 static assert(hasEvenLength!(1, 2, 3, 4)); 471 } 472 473 enum isSignedIntegral(T) = isIntegral!T && isSigned!T; 474 enum isUnsignedIntegral(T) = isIntegral!T && isUnsigned!T; 475 476 enum isString (T) = is(T == string); 477 enum isWString(T) = is(T == wstring); 478 enum isDString(T) = is(T == dstring); 479 480 enum isEnum(T) = is(T == enum); 481 @safe pure nothrow @nogc unittest 482 { 483 interface I {} 484 class A {} 485 class B( T ) {} 486 class C : B!int, I {} 487 struct S {} 488 enum E { X } 489 static assert(!isEnum!A ); 490 static assert(!isEnum!( B!int ) ); 491 static assert(!isEnum!C ); 492 static assert(!isEnum!I ); 493 static assert(isEnum!E ); 494 static assert(!isEnum!int ); 495 static assert(!isEnum!( int* ) ); 496 } 497 498 /* See_Also: http://d.puremagic.com/issues/show_bug.cgi?id=4427 */ 499 enum isStruct(T) = is(T == struct); 500 @safe pure nothrow @nogc unittest 501 { 502 interface I {} 503 class A {} 504 class B( T ) {} 505 class C : B!int, I {} 506 struct S {} 507 static assert(!isStruct!A ); 508 static assert(!isStruct!( B!int ) ); 509 static assert(!isStruct!C ); 510 static assert(!isStruct!I ); 511 static assert(isStruct!S ); 512 static assert(!isStruct!int ); 513 static assert(!isStruct!( int* ) ); 514 } 515 516 enum isClass(T) = is(T == class); 517 @safe pure nothrow @nogc unittest 518 { 519 interface I {} 520 class A {} 521 class B( T ) {} 522 class C : B!int, I {} 523 struct S {} 524 static assert(isClass!A ); 525 static assert(isClass!( B!int ) ); 526 static assert(isClass!C ); 527 static assert(!isClass!I ); 528 static assert(!isClass!S ); 529 static assert(!isClass!int ); 530 static assert(!isClass!( int* ) ); 531 } 532 533 enum isInterface(T) = is(T == interface); 534 @safe pure nothrow @nogc unittest 535 { 536 interface I {} 537 class A {} 538 class B( T ) {} 539 class C : B!int, I {} 540 struct S {} 541 static assert(!isInterface!A ); 542 static assert(!isInterface!( B!int ) ); 543 static assert(!isInterface!C ); 544 static assert(isInterface!I ); 545 static assert(!isInterface!S ); 546 static assert(!isInterface!int ); 547 static assert(!isInterface!( int* ) ); 548 } 549 550 template isType(T) { enum isType = true; } 551 template isType(alias T) { enum isType = false; } 552 553 @safe pure nothrow @nogc unittest 554 { 555 struct S { alias int foo; } 556 static assert(isType!int ); 557 static assert(isType!float ); 558 static assert(isType!string ); 559 //static assert(isType!S ); // Bugzilla 4431 560 static assert(isType!( S.foo ) ); 561 static assert(!isType!4 ); 562 static assert(!isType!"Hello world!" ); 563 } 564 565 enum nameOf(alias a) = a.stringof; 566 /// 567 @safe pure nothrow @nogc unittest 568 { 569 int var; 570 static assert(nameOf!var == var.stringof); 571 } 572 573 /** Is $(D ElementType) of type of $(D a). */ 574 alias ElementTypeOf(alias a) = ElementType!(typeof(a)); 575 /// 576 @safe pure nothrow @nogc unittest 577 { 578 int[] var; 579 static assert(is(ElementTypeOf!var == int)); 580 } 581 582 template Chainable() 583 { 584 import std.range: chain; 585 auto ref opCast(Range)(Range r) 586 { 587 return chain(this, r); 588 } 589 } 590 @safe pure nothrow @nogc unittest { mixin Chainable; } 591 592 /** Returns true if `T` is an instance of the template `S`. 593 See_Also: http://forum.dlang.org/thread/mailman.2901.1316118301.14074.digitalmars-d-learn@puremagic.com#post-zzdpfhsgfdgpszdbgbbt:40forum.dlang.org 594 */ 595 template isA(alias S, T) 596 { 597 import std.traits : isInstanceOf; 598 enum isA = isInstanceOf!(S, T); 599 } 600 601 @safe pure nothrow @nogc unittest 602 { 603 import std.traits : isInstanceOf; 604 import std.range : SortedRange, assumeSorted; 605 const x = [1, 2, 3].s[].assumeSorted; 606 static assert(isInstanceOf!(SortedRange, typeof(x))); 607 static assert(isA!(SortedRange, typeof(x))); 608 } 609 610 /** See_Also: http://forum.dlang.org/thread/bug-6384-3@http.d.puremagic.com/issues/ 611 See_Also: http://forum.dlang.org/thread/jrqiiicmtpenzokfxvlz@forum.dlang.org */ 612 enum isOpBinary(T, string op, U) = is(typeof(mixin("T.init" ~ op ~ "U.init"))); 613 614 enum isComparable(T) = is(typeof({ return T.init < T.init; })); /// TODO Move to Phobos' std.traits 615 enum isEquable (T) = is(typeof({ return T.init == T.init; })); /// TODO Move to Phobos' std.traits 616 enum isNotEquable(T) = is(typeof({ return T.init != T.init; })); /// TODO Move to Phobos' std.traits 617 618 @safe pure nothrow @nogc unittest 619 { 620 static assert(isComparable!int); 621 static assert(isComparable!string); 622 static assert(!isComparable!creal); 623 static struct Foo {} 624 static assert(!isComparable!Foo); 625 static struct Bar { bool opCmp(Bar) { return true; } } 626 static assert(isComparable!Bar); 627 } 628 629 // TODO variadic 630 enum areComparable(T, U) = is(typeof({ return T.init < U.init; })); /// TODO Move to Phobos' std.traits 631 enum areEquable (T, U) = is(typeof({ return T.init == U.init; })); /// TODO Move to Phobos' std.traits 632 enum areNotEquable(T, U) = is(typeof({ return T.init != U.init; })); /// TODO Move to Phobos' std.traits 633 634 @safe pure nothrow @nogc unittest 635 { 636 static assert(areComparable!(int, float)); 637 static assert(areEquable!(int, float)); 638 static assert(areNotEquable!(int, float)); 639 640 static assert(!areComparable!(int, string)); 641 static assert(!areEquable!(int, string)); 642 static assert(!areNotEquable!(int, string)); 643 } 644 645 enum isValueType(T) = !hasIndirections!T; 646 // enum isValueType(T) = isScalarType!T || isStaticArray!T || isStruct!T; 647 enum hasValueSemantics(T) = !hasIndirections!T; // TODO merge with isValueType 648 649 enum isReferenceType(T) = hasIndirections!T; 650 651 enum arityMin0(alias fun) = __traits(compiles, fun()); 652 653 /** TODO Unite into a variadic. 654 See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org 655 */ 656 enum isCallableWith(alias fun, T) = (is(typeof(fun(T.init))) || 657 is(typeof(T.init.fun))); // TODO Are both these needed? 658 @safe pure nothrow @nogc unittest 659 { 660 auto sqr(T)(T x) { return x*x; } 661 assert(isCallableWith!(sqr, int)); 662 assert(!isCallableWith!(sqr, string)); 663 } 664 665 /* TODO Unite into a variadic. 666 See_Also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org 667 */ 668 enum isCallableWith(alias fun, T, U) = (is(typeof(fun(T.init, 669 U.init))) || 670 is(typeof(T.init.fun(U)))); // TODO Are both these needed? 671 @safe pure nothrow @nogc unittest 672 { 673 auto sqr2(T)(T x, T y) { return x*x + y*y; } 674 assert(isCallableWith!(sqr2, int, int)); 675 assert(!isCallableWith!(sqr2, int, string)); 676 } 677 678 /** Check if `T` is a Sorted Range. 679 See_Also: http://forum.dlang.org/thread/lt1g3q$15fe$1@digitalmars.com 680 */ 681 template isSortedRange(T) 682 { 683 import std.traits : isInstanceOf; 684 import std.range: SortedRange; 685 enum isSortedRange = isInstanceOf!(SortedRange, T); // TODO Or use: __traits(isSame, TemplateOf!R, SortedRange) 686 } 687 688 /** Check if Function $(D expr) is callable at compile-time. 689 See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org 690 */ 691 template isCTFEable(alias fun) 692 { 693 template isCTFEable_aux(alias T) 694 { 695 enum isCTFEable_aux = T; 696 } 697 enum isCTFEable = __traits(compiles, isCTFEable_aux!(fun())); 698 } 699 700 template isCTFEable2(fun...) 701 { 702 enum isCTFEable2 = true; 703 } 704 705 @safe pure nothrow unittest 706 { 707 int fun1() { return 1; } 708 auto fun1_N() 709 { 710 import std.array; 711 //would return Error: gc_malloc cannot be interpreted at compile time, 712 /* because it has no available source code due to a bug */ 713 return [1].array; 714 } 715 int fun2(int x) 716 { 717 return 1; 718 } 719 auto fun2_N(int x){ 720 import std.array; 721 //same as fun1_N 722 return [1].array; 723 } 724 725 int a1; 726 enum a2=0; 727 728 static assert(!isCTFEable!(()=>a1)); 729 static assert(isCTFEable!(()=>a2)); 730 731 static assert(isCTFEable!fun1); 732 /* static assert(!isCTFEable!fun1_N); */ 733 734 static assert(isCTFEable!(()=>fun2(0))); 735 /* static assert(!isCTFEable!(()=>fun2_N(0))); */ 736 //NOTE:an alternate syntax which could be implemented would be: static 737 /* assert(!isCTFEable!(fun2_N,0)); */ 738 } 739 740 /** Check if the value of $(D expr) is known at compile-time. 741 See_Also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org 742 */ 743 enum isCTEable(alias expr) = __traits(compiles, { enum id = expr; }); 744 745 @safe pure nothrow @nogc unittest 746 { 747 static assert(isCTEable!11); 748 enum x = 11; 749 static assert(isCTEable!x); 750 auto y = 11; 751 static assert(!isCTEable!y); 752 } 753 754 import std.traits: hasFunctionAttributes, isCallable, ParameterTypeTuple, Unqual; 755 756 /** Returns $(D true) if `T` is not $(D const) or $(D immutable). 757 Note that isConst is true for string, or immutable(char)[], because the 758 'head' is mutable. 759 */ 760 import std.traits : isMutable; 761 enum isConst(T) = !isMutable!T; 762 763 @safe pure nothrow @nogc unittest 764 { 765 static assert(isConst!(const(int))); 766 static assert(!isConst!int); 767 } 768 769 import std.traits : CommonType; 770 771 /// Is `true` iff `Types` all share a common type. 772 enum bool haveCommonType(Types...) = !is(CommonType!Types == void); 773 774 /// 775 @safe pure nothrow @nogc unittest 776 { 777 static assert(haveCommonType!(bool, int, long)); 778 static assert(!haveCommonType!(bool, int, string)); 779 } 780 781 /** Check if $(D fun) is a pure function. */ 782 enum bool isPure(alias fun) = hasFunctionAttributes!(fun, `pure`); 783 784 /** Check if $(D fun) is a function purely callable with arguments T. */ 785 enum bool isPurelyCallableWith(alias fun, T...) = (isPure!fun && 786 is(T == ParameterTypeTuple!fun)); 787 788 /// 789 @safe pure nothrow @nogc unittest 790 { 791 static int foo(int x) @safe pure nothrow { return x; } 792 static assert(isPure!foo); 793 static assert(isPurelyCallableWith!(foo, int)); 794 } 795 796 /** Check if $(D fun) is a @nogc function. 797 See_Also: http://forum.dlang.org/thread/dyumjfmxmstpgyxbozry@forum.dlang.org 798 */ 799 enum bool isNogc(alias fun) = hasFunctionAttributes!(fun, `@nogc`); 800 801 /// 802 @safe pure nothrow @nogc unittest 803 { 804 static int foo(int x) @nogc pure nothrow; 805 static int goo(int x) pure nothrow; 806 static assert(isNogc!foo); 807 static assert(!isNogc!goo); 808 } 809 810 /** Persistently Call Function $(D fun) with arguments $(D args). 811 812 Hash Id Build-Timestamp (Code-Id because we currently have stable way of hashing-algorithms) is Constructed from Data Structure: 813 - Hierarchically Mangled Unqual!typeof(instance) 814 - Use msgpack in combination with sha1Of or only sha1Of (with extended 815 overloads for sha1Of) if available. 816 817 Extend std.functional : memoize to accept pure functions that takes an 818 immutable mmap as input. Create wrapper that converts file to immutable mmap 819 and performs memoization on the pure function. 820 821 */ 822 auto persistentlyMemoizedCall(alias fun, T...)(T args) 823 if (isPure!fun && 824 isCallable!(fun, args)) 825 { 826 import std.functional: memoize; 827 return fun(args); 828 } 829 830 /** Move std.uni.newLine? 831 TODO What to do with Windows style endings? 832 See_Also: https://en.wikipedia.org/wiki/Newline 833 */ 834 bool isNewline(C)(C c) @safe pure nothrow @nogc 835 if (isSomeChar!C) 836 { 837 import std.ascii: newline; // TODO Probably not useful. 838 static if (newline == "\n") 839 { 840 return (c == '\n' || c == '\r'); // optimized for systems with \n as default 841 } 842 else static if (newline == "\r") 843 { 844 return (c == '\r' || c == '\n'); // optimized for systems with \r as default 845 } 846 else 847 { 848 static assert(0, "Support Windows?"); 849 } 850 } 851 852 bool isNewline(S)(S s) @safe pure nothrow @nogc 853 if (isSomeString!S) 854 { 855 import std.ascii: newline; // TODO Probably not useful. 856 static if (newline == "\n") 857 { 858 return (s == '\n' || s == '\r'); // optimized for systems with \n as default 859 } 860 else static if (newline == "\r") 861 { 862 return (s == '\r' || s == '\n'); // optimized for systems with \r as default 863 } 864 else static if (newline == "\r\n") 865 { 866 return (s == "\r\n" || s == '\r' || s == '\n'); // optimized for systems with \r\n as default 867 } 868 else static if (newline == "\n\r") 869 { 870 return (s == "\n\r" || s == '\r' || s == '\n'); // optimized for systems with \n\r as default 871 } 872 else 873 { 874 static assert(0, "Support windows?"); 875 } 876 } 877 878 /** Dynamic variant of $(D EnumMembers) returning enum member constants 879 * (enumerators) of `T`. 880 * 881 * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org 882 */ 883 T[] enumMembersAsEnumerators(T)() 884 if (is(T == enum)) 885 { 886 import std.array : Appender; 887 Appender!(T[]) members; // TODO use static array instead 888 enum maxLength = T.max - T.min + 1; // possibly overestimate of final length needed 889 members.reserve(maxLength); 890 foreach (const member; __traits(allMembers, T)) 891 { 892 members.put(__traits(getMember, T, member)); 893 } 894 return members.data[]; 895 } 896 897 /** Dynamic Variant of $(D EnumMembers) excluding the enumerator aliases. 898 * 899 * See_Also: http://forum.dlang.org/post/ziappmtvucmuefphblse@forum.dlang.org 900 * See_Also: http://forum.dlang.org/post/awihyvzjswwayeqtklly@forum.dlang.org 901 * See_Also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org 902 * See_Also: https://issues.dlang.org/show_bug.cgi?id=10951 903 */ 904 auto uniqueEnumMembers(T)() @trusted 905 if (is(T == enum)) 906 { 907 import std.array : Appender; 908 Appender!(T[]) uniqueMembers; 909 enum maxLength = T.max - T.min + 1; // possibly overestimate of final length 910 uniqueMembers.reserve(maxLength); 911 enum maxBitCount = ((maxLength / (8*size_t.sizeof)) + 912 (maxLength % (8*size_t.sizeof) ? 1 : 0)); 913 size_t[maxBitCount] uniqueBits; // dense set representation of enumerators 914 foreach (const member; __traits(allMembers, T)) 915 { 916 const memberEnumerator = __traits(getMember, T, member); 917 const member_ = cast(size_t)memberEnumerator; 918 import core.bitop : bt, bts; 919 if (!bt(&uniqueBits[0], member_)) 920 { 921 uniqueMembers.put(memberEnumerator); 922 bts(&uniqueBits[0], member_); 923 } 924 } 925 return uniqueMembers.data[]; 926 } 927 928 /// 929 @safe pure nothrow /*@nogc*/ unittest 930 { 931 enum E { x, y, z, Z = z, Y = y } 932 import std.algorithm.comparison : equal; 933 assert(enumMembersAsEnumerators!E.equal([E.x, E.y, E.z, E.Z, E.Y])); // run-time 934 assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // run-time 935 // static assert(uniqueEnumMembers!E.equal([E.x, E.y, E.z])); // compile-time 936 static assert(E.x == 0); 937 static assert(E.y == 1); 938 static assert(E.z == 2); 939 static assert(E.Z == E.z); 940 static assert(E.Y == E.y); 941 } 942 943 enum sizeOf(T) = T.sizeof; // TODO Add to Phobos 944 template sizesOf(T...) // TODO Add to Phobos 945 { 946 import std.meta : staticMap; 947 enum sizesOf = staticMap!(sizeOf, T); 948 } 949 950 /// 951 @safe pure nothrow unittest 952 { 953 enum sizes = sizesOf!(bool, short, int, long); 954 955 // static use 956 static assert(sizes[0] == 1); 957 static assert(sizes[1] == 2); 958 static assert(sizes[2] == 4); 959 static assert(sizes[3] == 8); 960 961 // dynamic use 962 const i = 0; 963 assert([sizes][i] == 1); 964 } 965 966 enum stringOf(T) = T.stringof; // TODO Add to Phobos 967 template stringsOf(T...) // TODO Add to Phobos 968 { 969 import std.meta : staticMap; 970 enum stringsOf = staticMap!(stringOf, T); 971 } 972 973 /// 974 @safe pure nothrow @nogc unittest 975 { 976 enum strings = stringsOf!(bool, short, int, long); 977 } 978 979 /** Get Dimensionality of Type `T`. 980 See_Also: http://forum.dlang.org/thread/hiuhqdxtpifhzwebewjh@forum.dlang.org?page=2 981 */ 982 983 template dimensionality (T) 984 { 985 import std.range.primitives : isInputRange; 986 template count_dim (uint i = 0) 987 { 988 static if (is(typeof(T.init.opSlice!i(0, 0)))) 989 { 990 enum count_dim = count_dim!(i+1); 991 } 992 else static if (i == 0 && 993 (isInputRange!T || 994 is(typeof(T.init[0])))) 995 { 996 enum count_dim = 1; 997 } 998 else 999 { 1000 enum count_dim = i; 1001 } 1002 } 1003 alias dimensionality = count_dim!(); 1004 } 1005 1006 /// 1007 @safe pure nothrow @nogc unittest 1008 { 1009 static assert(dimensionality!(int[]) == 1); 1010 } 1011 1012 /// Rank of type `T`. 1013 template rank(T) 1014 { 1015 import std.range.primitives : isInputRange; 1016 static if (isInputRange!T) // is T a range? 1017 enum rank = 1 + rank!(ElementType!T); // if yes, recurse 1018 else 1019 enum rank = 0; // base case, stop there 1020 } 1021 1022 /// 1023 @safe pure nothrow @nogc unittest 1024 { 1025 import std.range : cycle; 1026 1027 auto c = cycle([[0,1].s[], 1028 [2,3].s[]].s[]); // == [[0,1],[2,3],[0,1],[2,3],[0,1]... 1029 1030 assert(rank!(typeof(c)) == 2); // range of ranges 1031 1032 static assert(rank!(int[]) == 1); 1033 static assert(rank!(int[][]) == 2); 1034 } 1035 1036 /// Returns: `true` iff `T` is a template instance, `false` otherwise. 1037 template isTemplateInstance(T) 1038 { 1039 import std.traits : TemplateOf; 1040 enum isTemplateInstance = is(typeof(TemplateOf!(T))); 1041 } 1042 1043 /// 1044 @safe pure nothrow @nogc unittest 1045 { 1046 struct S(T) { T x; } 1047 static assert(isTemplateInstance!(S!int)); 1048 static assert(!isTemplateInstance!(int)); 1049 } 1050 1051 /** Get identifier (name) string of template instance `I`, or `null` if `I` is 1052 not a template instance. */ 1053 template templateIdentifierOf(I) 1054 { 1055 import std.traits : TemplateOf; 1056 static if (isTemplateInstance!I) 1057 { 1058 enum templateIdentifierOf = __traits(identifier, TemplateOf!I); 1059 } 1060 else 1061 { 1062 enum templateIdentifierOf = null; 1063 } 1064 } 1065 alias templateNameOf = templateIdentifierOf; 1066 1067 /// 1068 @safe pure nothrow @nogc unittest 1069 { 1070 struct S(T) { T x; } 1071 static assert(templateIdentifierOf!(S!int) == "S"); 1072 static assert(templateIdentifierOf!(int) == null); 1073 } 1074 1075 /** Get entropy in number of bits of `T`. */ 1076 template EntropyBitsOf(T) 1077 { 1078 import std.traits : isAggregateType, isArray; 1079 static if (isAggregateType!T) 1080 { 1081 // foreach (memberName; __traits(allMembers, T)) // for each member name in `struct TypedKey` 1082 // { 1083 // const member = __traits(getMember, T.init, memberName); // member 1084 // } 1085 enum EntropyBitsOf = 8*T.sizeof; 1086 } 1087 else 1088 { 1089 enum EntropyBitsOf = 8*T.sizeof; 1090 } 1091 } 1092 1093 /// 1094 @safe pure nothrow @nogc unittest 1095 { 1096 static assert(EntropyBitsOf!int == 8*int.sizeof); 1097 } 1098 1099 /** Is `true` if `sym` is an l-value, `false` otherwise. 1100 See_Also: https://forum.dlang.org/post/mailman.4192.1454351296.22025.digitalmars-d-learn@puremagic.com 1101 TODO Add to Phobos 1102 */ 1103 enum isLvalue(alias sym) = is(typeof((ref _){}(sym))); 1104 1105 /** Is `true` if `sym` is an l-value, `false` otherwise. 1106 */ 1107 enum isRvalue(alias sym) = !isLvalue!sym; 1108 1109 /// 1110 @safe pure nothrow @nogc unittest 1111 { 1112 int i; 1113 string s; 1114 static assert(isLvalue!i); 1115 static assert(isLvalue!s); 1116 static assert(!isLvalue!13); 1117 static assert(!isLvalue!"a"); 1118 } 1119 1120 template ownsItsElements(C) 1121 { 1122 import std.traits : hasIndirections; 1123 import std.range.primitives : ElementType; 1124 enum ownsItsElements = !__traits(isCopyable, C) && !hasIndirections!(ElementType!C); 1125 } 1126 1127 /** Copied from private definition in Phobos' std.meta. 1128 */ 1129 private template isSame(ab...) 1130 if (ab.length == 2) 1131 { 1132 static if (__traits(compiles, expectType!(ab[0]), 1133 expectType!(ab[1]))) 1134 { 1135 enum isSame = is(ab[0] == ab[1]); 1136 } 1137 else static if (!__traits(compiles, expectType!(ab[0])) && 1138 !__traits(compiles, expectType!(ab[1])) && 1139 __traits(compiles, expectBool!(ab[0] == ab[1]))) 1140 { 1141 static if (!__traits(compiles, &ab[0]) || 1142 !__traits(compiles, &ab[1])) 1143 enum isSame = (ab[0] == ab[1]); 1144 else 1145 enum isSame = __traits(isSame, ab[0], ab[1]); 1146 } 1147 else 1148 { 1149 enum isSame = __traits(isSame, ab[0], ab[1]); 1150 } 1151 } 1152 private template expectType(T) {} 1153 private template expectBool(bool b) {} 1154 1155 template allSatisfyIterative(alias F, T...) 1156 { 1157 static foreach (Ti; T) 1158 { 1159 static if (is(typeof(allSatisfyIterative) == void) && // not yet defined 1160 !F!(Ti)) 1161 { 1162 enum allSatisfyIterative = false; 1163 } 1164 } 1165 static if (is(typeof(allSatisfyIterative) == void)) // if not yet defined 1166 { 1167 enum allSatisfyIterative = true; 1168 } 1169 } 1170 1171 /// 1172 @safe unittest 1173 { 1174 import std.traits : isIntegral; 1175 1176 static assert( allSatisfyIterative!(isIntegral)); 1177 static assert( allSatisfyIterative!(isIntegral, int)); 1178 static assert(!allSatisfyIterative!(isIntegral, int, double)); 1179 static assert( allSatisfyIterative!(isIntegral, int, long)); 1180 static assert(!allSatisfyIterative!(isIntegral, string)); 1181 } 1182 1183 template anySatisfyIterative(alias F, T...) 1184 { 1185 static foreach (Ti; T) 1186 { 1187 static if (is(typeof(anySatisfyIterative) == void) && // not yet defined 1188 F!(Ti)) 1189 { 1190 enum anySatisfyIterative = true; 1191 } 1192 } 1193 static if (is(typeof(anySatisfyIterative) == void)) // if not yet defined 1194 { 1195 enum anySatisfyIterative = false; 1196 } 1197 } 1198 1199 /// 1200 @safe unittest 1201 { 1202 import std.traits : isIntegral; 1203 1204 static assert(!anySatisfyIterative!(isIntegral)); 1205 static assert( anySatisfyIterative!(isIntegral, int)); 1206 static assert(!anySatisfyIterative!(isIntegral, string, double)); 1207 static assert( anySatisfyIterative!(isIntegral, int, double)); 1208 static assert( anySatisfyIterative!(isIntegral, int, string)); 1209 } 1210 1211 version(unittest) 1212 { 1213 import std.typecons : Tuple; 1214 import nxt.array_help : s; 1215 } 1216 1217 /** Is `true` iff `T` has a property member non-function named `name`. */ 1218 template hasPropertyFunction(T, string name) 1219 { 1220 static if (__traits(hasMember, T, name)) 1221 { 1222 enum hasPropertyFunction = (!is(typeof(__traits(getMember, T, name)) == function) && 1223 __traits(getOverloads, T, name).length); 1224 } 1225 else 1226 { 1227 enum hasPropertyFunction = false; 1228 } 1229 } 1230 1231 /// 1232 unittest 1233 { 1234 struct S 1235 { 1236 int m; 1237 static int sm; 1238 1239 void f() {} 1240 static void sf() {} 1241 1242 @property int rp() { return m; } 1243 @property void wp(int) {} 1244 } 1245 1246 static assert(hasPropertyFunction!(S, "rp")); 1247 static assert(hasPropertyFunction!(S, "wp")); 1248 1249 static assert(!hasPropertyFunction!(S, "na")); 1250 static assert(!hasPropertyFunction!(S, "m")); 1251 static assert(!hasPropertyFunction!(S, "sm")); 1252 static assert(!hasPropertyFunction!(S, "f")); 1253 static assert(!hasPropertyFunction!(S, "sf")); 1254 } 1255 1256 /** Is `true` if `T.name` is a manifest constant, built-in type field, or 1257 * immutable static. 1258 */ 1259 template isManifestAssignable(T, string name) 1260 { 1261 enum isManifestAssignable = is(typeof({ enum x = mixin("T." ~ name); })); 1262 } 1263 1264 /// 1265 unittest 1266 { 1267 struct A 1268 { 1269 int m; 1270 static immutable int sim = 1; 1271 enum e = 1; 1272 } 1273 static assert(!isManifestAssignable!(A*, "na")); 1274 static assert(!isManifestAssignable!(A, "na")); 1275 static assert(!isManifestAssignable!(A, "m")); 1276 static assert(isManifestAssignable!(A, "e")); 1277 static assert(isManifestAssignable!(A, "sim")); 1278 } 1279 1280 /** Tells you if a name is a read and/or write property 1281 * 1282 * Returns: `Tuple!(bool, "isRead", bool, "isWrite")` 1283 */ 1284 auto propertySemantics(T, string name)() 1285 if (hasPropertyFunction!(T, name)) 1286 { 1287 import std.typecons : tuple; 1288 1289 enum overloads = __traits(getOverloads, T, name).length; 1290 enum canInstantiateAsField = is(typeof(mixin("T.init." ~ name))); 1291 1292 static if (overloads > 1 || canInstantiateAsField) 1293 { 1294 enum canRead = true; 1295 } 1296 else 1297 { 1298 enum canRead = false; 1299 } 1300 static if (overloads > 1 || !canInstantiateAsField) 1301 { 1302 enum canWrite = true; 1303 } 1304 else 1305 { 1306 enum canWrite = false; 1307 } 1308 1309 return tuple!("canRead", "canWrite")(canRead, canWrite); 1310 } 1311 1312 /// 1313 unittest 1314 { 1315 import std.typecons; 1316 1317 struct S 1318 { 1319 int m; 1320 @property int rp() 1321 { 1322 return m; 1323 } 1324 1325 @property void wp(int) 1326 { 1327 } 1328 1329 @property int rwp() 1330 { 1331 return m; 1332 } 1333 1334 @property void rwp(int) 1335 { 1336 } 1337 } 1338 1339 static assert(!__traits(compiles, propertySemantics!(S, "na"))); 1340 static assert(!__traits(compiles, propertySemantics!(S, "m"))); 1341 1342 static assert(propertySemantics!(S, "rp") == tuple!("canRead", "canWrite")(true, false)); 1343 static assert(propertySemantics!(S, "wp") == tuple!("canRead", "canWrite")(false, true)); 1344 static assert(propertySemantics!(S, "rwp") == tuple!("canRead", "canWrite")(true, true)); 1345 } 1346 1347 /** Is `true` iff the postblit of `T` is disabled (`@disable this(this)`). 1348 * 1349 * See_Also: https://forum.dlang.org/post/dkohvpbmakbdbhnmnmbg@forum.dlang.org 1350 */ 1351 template hasDisabledPostblit(T) 1352 { 1353 static if (__traits(hasMember, T, "__postblit")) 1354 { 1355 enum hasDisabledPostblit = __traits(isDisabled, T.__postblit); 1356 } 1357 else 1358 { 1359 enum hasDisabledPostblit = false; 1360 } 1361 } 1362 1363 /// 1364 @safe pure unittest 1365 { 1366 static struct S 1367 { 1368 @disable this(this); 1369 } 1370 static assert(!hasDisabledPostblit!int); 1371 static assert( hasDisabledPostblit!S); 1372 } 1373 1374 template isSubclassOf(Class, BaseClass) 1375 { 1376 import std.traits : BaseClassesTuple; 1377 alias BaseClasses = BaseClassesTuple!Class; 1378 import std.meta : staticIndexOf; 1379 enum isSubclassOf = staticIndexOf!(BaseClass, BaseClasses) != -1; 1380 } 1381 1382 /// 1383 @safe pure unittest 1384 { 1385 class X {} 1386 class Y : X {} 1387 1388 static assert(!isSubclassOf!(X, Y)); 1389 static assert( isSubclassOf!(X, Object)); 1390 1391 static assert( isSubclassOf!(Y, X)); 1392 static assert( isSubclassOf!(Y, Object)); 1393 }