1 /** 2 Numem Traits 3 4 Copyright: 5 Copyright © 2005-2009, The D Language Foundation. 6 Copyright © 2023-2025, Kitsunebi Games 7 Copyright © 2023-2025, Inochi2D Project 8 9 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 10 Authors: 11 $(HTTP digitalmars.com, Walter Bright), 12 Tomasz Stachowiak (isExpressions), 13 $(HTTP erdani.org, Andrei Alexandrescu), 14 Shin Fujishiro, 15 $(HTTP octarineparrot.com, Robert Clipsham), 16 $(HTTP klickverbot.at, David Nadlinger), 17 Kenji Hara, 18 Shoichi Kato 19 Luna Nielsen 20 */ 21 module numem.core.traits; 22 import numem.core.meta; 23 24 /** 25 Gets a sequence over all of the fields in type $(D T). 26 27 If $(D T) is a type with no fields, returns a sequence containing the input. 28 */ 29 template Fields(T) { 30 static if(is(T == struct) || is(T == union)) 31 alias Fields = typeof(T.tupleof[0..$-__traits(isNested, T)]); 32 else static if (is(T == class) || is(T == interface)) 33 alias Fields = typeof(T.tupleof); 34 else 35 alias Fields = AliasSeq!T; 36 } 37 38 /** 39 Gets the base element type of type $(D T). 40 */ 41 template BaseElemOf(T) { 42 static if(is(OriginalType!T == E[N], E, size_t N)) 43 alias BaseElemOf = BaseElemOf!E; 44 else 45 alias BaseElemOf = T; 46 } 47 48 /** 49 Gets the original type of $(D T). 50 */ 51 template OriginalType(T) { 52 template Impl(T) { 53 static if(is(T U == enum)) alias Impl = OriginalType!U; 54 else alias Impl = T; 55 } 56 57 alias OriginalType = ModifyTypePreservingTQ!(Impl, T); 58 } 59 60 /** 61 Modifies type $(D T) to follow the predicate specified by $(D Modifier). 62 */ 63 template ModifyTypePreservingTQ(alias Modifier, T) { 64 static if (is(T U == immutable U)) alias ModifyTypePreservingTQ = immutable Modifier!U; 65 else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U; 66 else static if (is(T U == shared inout U)) alias ModifyTypePreservingTQ = shared inout Modifier!U; 67 else static if (is(T U == shared const U)) alias ModifyTypePreservingTQ = shared const Modifier!U; 68 else static if (is(T U == shared U)) alias ModifyTypePreservingTQ = shared Modifier!U; 69 else static if (is(T U == inout const U)) alias ModifyTypePreservingTQ = inout const Modifier!U; 70 else static if (is(T U == inout U)) alias ModifyTypePreservingTQ = inout Modifier!U; 71 else static if (is(T U == const U)) alias ModifyTypePreservingTQ = const Modifier!U; 72 else alias ModifyTypePreservingTQ = Modifier!T; 73 } 74 75 /** 76 Removes const type qualifiers from $(D T). 77 */ 78 alias Unconst(T : const U, U) = U; 79 80 /** 81 Removes shared type qualifiers from $(D T). 82 */ 83 alias Unshared(T : shared U, U) = U; 84 85 /** 86 Removes all qualifiers from type T. 87 */ 88 template Unqual(T : const U, U) { 89 static if(is(U == shared V, V)) 90 alias Unqual = V; 91 else 92 alias Unqual = U; 93 } 94 95 /** 96 Gets the reference type version of type $(D T). 97 */ 98 template Ref(T) { 99 static if (is(T == class) || isHeapAllocated!T) 100 alias Ref = T; 101 else 102 alias Ref = T*; 103 } 104 105 /** 106 Gets the value type version of type $(D T). 107 */ 108 template Unref(T) { 109 static if (!isClasslike!T && isHeapAllocated!T) 110 alias Unref = typeof(*T.init); 111 else 112 alias Unref = T; 113 } 114 115 /** 116 Gets the amount of bytes needed to allocate an instance of type $(D T). 117 */ 118 template AllocSize(T) { 119 static if (is(T == class) || is(T == interface)) 120 enum AllocSize = __traits(classInstanceSize, T); 121 else 122 enum AllocSize = T.sizeof; 123 } 124 125 /** 126 Gets the alignment of type $(D T) in bytes. 127 */ 128 template AllocAlign(T) { 129 static if(is(T == class)) 130 enum AllocAlign = __traits(classInstanceAlignment, T); 131 else 132 enum AllocAlign = T.alignof; 133 } 134 135 private struct __DummyStruct { } 136 137 /** 138 Returns the rvalue equivalent of T. 139 */ 140 @property T rvalueOf(T)(T val) { return val; } 141 142 /** 143 Returns the rvalue equivalent of $(D T). 144 145 Can only be used at compile time for type checking. 146 */ 147 @property T rvalueOf(T)(inout __DummyStruct = __DummyStruct.init); 148 149 /** 150 Returns the lvalue equivalent of $(D T). 151 152 Can only be used at compile time for type checking. 153 */ 154 @property ref T lvalueOf(T)(inout __DummyStruct = __DummyStruct.init); 155 156 /** 157 Gets whether $(D T) supports moving. 158 */ 159 enum isMovable(T) = 160 (is(T == struct) || is(T == union)) || 161 (__traits(isStaticArray, T) && hasElaborateMove!(T.init[0])); 162 163 /** 164 Gets whether $(D T) is an aggregate type (i.e. a type which contains other types) 165 */ 166 enum isAggregateType(T) = 167 is(T == class) || is(T == interface) || 168 is(T == struct) || is(T == union); 169 170 /** 171 Gets whether $(D T) is a class-like (i.e. class or interface) 172 */ 173 enum isClasslike(T) = 174 is(T == class) || is(T == interface); 175 176 /** 177 Gets whether $(D T) is a struct-like (i.e. struct or union) 178 */ 179 enum isStructLike(T) = 180 is(T == struct) || is(T == union); 181 182 /** 183 Gets whether $(D T) is a type. 184 */ 185 enum isType(T) = !is(typeof(T)) || is(T == void); 186 187 /** 188 Gets whether the provided type is a scalar type. 189 */ 190 enum isScalarType(T) = __traits(isScalar, T) && is(T : real); 191 192 /** 193 Gets whether the provided type is a basic type. 194 */ 195 enum isBasicType(T) = isScalarType!T || is(immutable T == immutable void); 196 197 /** 198 Gets whether $(D T) is a pointer type. 199 */ 200 enum isPointer(T) = 201 is(T == U*, U) && !isClasslike!T; 202 203 /** 204 Gets whether $(D T) is heap allocated. 205 */ 206 enum isHeapAllocated(T) = 207 is(T == class) || is(T == U*, U); 208 209 /** 210 Gets whether type $(D T) is an array. 211 */ 212 enum isArray(T) = is(T == E[n], E, size_t n); 213 214 /** 215 Gets whether type T is a floating point type. 216 */ 217 enum isFloatingPoint(T) = __traits(isFloating, T); 218 219 /** 220 Gets whether type T is a integral point type. 221 */ 222 enum isIntegral(T) = __traits(isIntegral, T); 223 224 /** 225 Gets whether type T is a numeric type. 226 */ 227 enum isNumeric(T) = 228 __traits(isFloating, T) && 229 __traits(isIntegral, T); 230 231 template FtoI(T) { 232 static if (is(T == double)) 233 alias FtoI = ulong; 234 else static if (is(T == float)) 235 alias FtoI = uint; 236 else 237 alias FtoI = size_t; 238 } 239 240 /** 241 Gets whether $(D Lhs) can be assigned to $(D Rhs). 242 */ 243 template isAssignable(Lhs, Rhs = Lhs) { 244 enum isAssignable = 245 __traits(compiles, lvalueOf!Lhs = rvalueOf!Rhs) && 246 __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs); 247 } 248 249 /** 250 Gets whether $(D Lhs) can be assigned to $(D Rhs) or $(D Rhs) can be assigned to $(D Lhs). 251 */ 252 enum isAnyAssignable(Lhs, Rhs = Lhs) = 253 isAssignable!(Lhs, Rhs) || isAssignable!(Rhs, Lhs); 254 255 /** 256 Gets whether the unqualified versions of $(D Lhs) and $(D Rhs) are in 257 any way compatible in any direction. 258 */ 259 enum isAnyCompatible(Lhs, Rhs) = 260 is(Unqual!Lhs : Unqual!Rhs) || is(Unqual!Rhs : Unqual!Lhs); 261 262 /** 263 Gets whether unqualified type $(D TValue) is compatible with the unqualified 264 range element type of $(D TRange). 265 */ 266 enum isAnyCompatibleRange(TRange, TValue) = is(TRange == U[], U) && isAnyCompatible!(typeof(TRange.init[0]), TValue); 267 268 /** 269 Gets whether $(D symbol) has the user defined attribute $(D attrib). 270 */ 271 template hasUDA(alias symbol, alias attrib) { 272 enum hasUDA = anySatisfy!(isDesiredAttr!attrib, __traits(getAttributes, symbol)); 273 } 274 275 /** 276 Gets a sequence of all of the attributes within attrib. 277 */ 278 template getUDAs(alias symbol, alias attrib) { 279 alias getUDAs = Filter!(isDesiredAttr!attrib, __traits(getAttributes, symbol)); 280 } 281 282 private 283 template isDesiredAttr(alias attribute) { 284 // Taken from phobos. 285 286 template isDesiredAttr(alias toCheck) { 287 static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) { 288 static if (__traits(compiles, toCheck == attribute)) 289 enum isDesiredAttr = toCheck == attribute; 290 else 291 enum isDesiredAttr = false; 292 } else static if (is(typeof(toCheck))) { 293 static if (__traits(isTemplate, attribute)) 294 enum isDesiredAttr = isInstanceOf!(attribute, typeof(toCheck)); 295 else 296 enum isDesiredAttr = is(typeof(toCheck) == attribute); 297 } else static if (__traits(isTemplate, attribute)) 298 enum isDesiredAttr = isInstanceOf!(attribute, toCheck); 299 else 300 enum isDesiredAttr = is(toCheck == attribute); 301 } 302 } 303 304 /** 305 Gets whether $(D T) is an instance of template $(D S). 306 307 Returns: 308 $(D true) if $(D T) is an instance of template $(D S), 309 $(D false) otherwise. 310 */ 311 enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...); 312 template isInstanceOf(alias S, alias T) { 313 enum impl(alias T : S!Args, Args...) = true; 314 enum impl(alias T) = false; 315 enum isInstanceOf = impl!T; 316 } /// ditto 317 318 /** 319 Gets whether $(D T) is a C++ aggregate type 320 */ 321 enum isCPP(T) = 322 isAggregateType!(Unref!T) && __traits(getLinkage, Unref!T) == "C++"; 323 324 /** 325 Gets whether $(D T) is an Objective-C class or protocol. 326 327 Additionally, said class or protocol needs to have the 328 $(D retain) and $(D release) methods. 329 */ 330 enum isObjectiveC(T) = 331 isClasslike!T && __traits(getLinkage, T) == "Objective-C"; 332 333 /** 334 Gets whether $(D T) is a *valid* NSObject derived 335 Objective-C class or protocol. 336 337 Said class or protocol needs to have the 338 $(D retain), $(D release) and $(D autorelease) methods. 339 340 See_Also: 341 $(LINK2 https://github.com/Inochi2D/objective-d, Objective-D) 342 */ 343 enum isValidObjectiveC(T) = 344 isClasslike!T && __traits(getLinkage, T) == "Objective-C" && 345 is(typeof(T.retain)) && is(typeof(T.release)); 346 347 348 /** 349 Gets whether $(D T) is an COM class or interface. 350 351 Additionally, said class or interface needs to have the 352 $(D retain) and $(D release) methods. 353 */ 354 enum isCOMClass(T) = 355 (is(T == class) || is(T == interface)) && 356 __traits(isCOMClass, T); 357 358 /** 359 Valid function names for `retain` type functions. 360 */ 361 enum rcRetainNames = AliasSeq!("retain", "Retain", "addRef", "AddRef"); 362 363 /** 364 Gets whether $(D T) has a `retain` style function. 365 366 Params: 367 T = The type to check 368 369 Returns: 370 $(D true) if $(D T) is a type with a retain function, 371 $(D false) otherwise. 372 373 See_Also: 374 $(D rcRetainNames) 375 */ 376 template hasRCRetainFunction(T) { 377 static if (isCOMClass!T || isValidObjectiveC!T) { 378 enum hasRCRetainFunction = true; 379 } else { 380 enum hasRCFunc(string name) = __traits(hasMember, T, name) && is(typeof((ref T obj) { mixin("obj.", name, "();"); })); 381 enum hasRCRetainFunction = anySatisfy!(hasRCFunc, rcRetainNames); 382 } 383 } 384 385 /** 386 Valid function names for `release` type functions. 387 */ 388 enum rcReleaseNames = AliasSeq!("release", "Release"); 389 390 /** 391 Gets whether $(D T) has a `retain` style function. 392 393 Params: 394 T = The type to check 395 396 Returns: 397 $(D true) if $(D T) is a type with a retain function, 398 $(D false) otherwise. 399 400 See_Also: 401 $(D rcRetainNames) 402 */ 403 template hasRCReleaseFunction(T) { 404 static if (isCOMClass!T || isValidObjectiveC!T) { 405 enum hasRCReleaseFunction = true; 406 } else { 407 enum hasRCFunc(string name) = __traits(hasMember, T, name) && is(typeof((ref T obj) { __traits(getMember, obj, name)(); })); 408 enum hasRCReleaseFunction = anySatisfy!(hasRCFunc, rcReleaseNames); 409 } 410 } 411 412 /** 413 Gets whether $(D T) is refcounted. 414 415 Params: 416 T = The type to query. 417 418 Returns: 419 $(D true) if $(D T) is a refcounted type, 420 $(D false) otherwise. 421 */ 422 template isRefcounted(T) { 423 static if (isCOMClass!T || isValidObjectiveC!T) { 424 enum isRefcounted = true; 425 } else { 426 enum isRefcounted = hasRCRetainFunction!T && hasRCReleaseFunction!T; 427 } 428 } 429 430 /** 431 Gets whether T is an inner class in a nested class layout. 432 */ 433 template isInnerClass(T) if(is(T == class)) { 434 static if (is(typeof(T.outer))) { 435 template hasOuterMember(T...) { 436 static if (T.length == 0) 437 enum hasOuterMember = false; 438 else 439 enum hasOuterMember = T[0] == "outer" || hasOuterMember!(T[1..$]); 440 } 441 442 enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) && !hasOuterMember!(__traits(allMembers, T)); 443 } else enum isInnerClass = false; 444 } 445 446 /** 447 Gets whether $(D T) or any of its children has an elaborate move. 448 */ 449 template hasElaborateMove(T) { 450 static if (isObjectiveC!T) 451 enum bool hasElaborateMove = false; 452 else static if (__traits(isStaticArray, T)) 453 enum bool hasElaborateMove = T.sizeof && hasElaborateMove!(BaseElemOf!T); 454 else static if (is(T == struct)) 455 enum hasElaborateMove = (is(typeof(S.init.opPostMove(lvalueOf!T))) && 456 !is(typeof(S.init.opPostMove(rvalueOf!T)))) || 457 anySatisfy!(.hasElaborateMove, Fields!T); 458 else 459 enum hasElaborateMove = false; 460 461 } 462 463 /** 464 Gets whether type $(D T) has elaborate assign semantics 465 (i.e. is $(D opAssign) declared for the type) 466 */ 467 template hasElaborateAssign(T) { 468 static if (isObjectiveC!T) 469 enum bool hasElaborateAssign = false; 470 else static if (__traits(isStaticArray, T)) 471 enum bool hasElaborateAssign = T.sizeof && hasElaborateAssign!(BaseElemOf!T); 472 else static if (is(T == struct)) 473 enum hasElaborateAssign = (is(typeof(S.init.opPostMove(opAssign!T))) && 474 !is(typeof(S.init.opPostMove(opAssign!T)))) || 475 anySatisfy!(.hasElaborateAssign, Fields!T); 476 else 477 enum hasElaborateAssign = false; 478 479 } 480 481 /** 482 Gets whether type $(D T) has elaborate copy constructor semantics 483 (i.e. is a copy constructor or postblit constructor declared.) 484 */ 485 template hasElaborateCopyConstructor(T) { 486 static if (isObjectiveC!T) 487 enum bool hasElaborateCopyConstructor = false; 488 else static if (__traits(isStaticArray, T)) 489 enum bool hasElaborateCopyConstructor = T.sizeof && hasElaborateCopyConstructor!(BaseElemOf!T); 490 else static if (is(T == struct)) 491 enum hasElaborateCopyConstructor = __traits(hasCopyConstructor, T) || __traits(hasPostblit, T); 492 else 493 enum hasElaborateCopyConstructor = false; 494 } 495 496 /** 497 Gets whether type $(D T) has elaborate destructor semantics (is ~this() declared). 498 */ 499 template hasElaborateDestructor(T) { 500 static if (__traits(isStaticArray, T)) 501 enum bool hasElaborateDestructor = T.sizeof && hasElaborateDestructor!(BaseElemOf!T); 502 else static if (is(T == struct)) 503 enum bool hasElaborateDestructor = is(typeof(() { T.init.__xdtor(); })); 504 else 505 enum bool hasElaborateDestructor = false; 506 } 507 508 /** 509 Gets whether type $(D T) has any destructor semantics, whether explicit 510 or implicit. 511 */ 512 template hasAnyDestructor(T) { 513 514 // NOTE: To handle self-referential structs we need this layer of indirection. 515 template isNotSelf(U) { 516 enum isNotSelf = !is(Unref!U == Unref!T); 517 } 518 519 static if (isObjectiveC!T) 520 enum bool hasAnyDestructor = false; 521 else static if (__traits(isStaticArray, T)) 522 enum bool hasAnyDestructor = T.sizeof && hasAnyDestructor!(BaseElemOf!T); 523 else static if (is(T == class)) // Classes have implicit destructors 524 enum bool hasAnyDestructor = true; 525 else static if (is(T == interface)) // Interfaces have implicit destructors 526 enum bool hasAnyDestructor = true; 527 else static if (is(Unref!T == struct)) { 528 static if (is(typeof(() { Unref!T.init.__xdtor(); }))) 529 enum bool hasAnyDestructor = true; 530 else enum bool hasAnyDestructor = anySatisfy!(.hasAnyDestructor, Filter!(isNotSelf, Fields!(Unref!T))); 531 } else 532 enum bool hasAnyDestructor = false; 533 } 534 535 /** 536 Detect whether symbol or type $(D T) is a function, a function pointer or a delegate. 537 538 Params: 539 T = The type to check 540 Returns: 541 A $(D_KEYWORD bool) 542 */ 543 enum bool isSomeFunction(alias T) = 544 is(T == return) || 545 is(typeof(T) == return) || 546 is(typeof(&T) == return); // @property 547 548 /** 549 Detect whether $(D T) is a callable object, which can be called with the 550 function call operator `$(LPAREN)...$(RPAREN)`. 551 */ 552 template isCallable(alias callable) { 553 static if (is(typeof(&callable.opCall) == delegate)) 554 // T is a object which has a member function opCall(). 555 enum bool isCallable = true; 556 else static if (is(typeof(&callable.opCall) V : V*) && is(V == function)) 557 // T is a type which has a static member function opCall(). 558 enum bool isCallable = true; 559 else static if (is(typeof(&callable.opCall!()) TemplateInstanceType)) 560 { 561 enum bool isCallable = isCallable!TemplateInstanceType; 562 } 563 else static if (is(typeof(&callable!()) TemplateInstanceType)) 564 { 565 enum bool isCallable = isCallable!TemplateInstanceType; 566 } 567 else 568 { 569 enum bool isCallable = isSomeFunction!callable; 570 } 571 } 572 573 /** 574 Get the function type from a callable object $(D func), or from a function pointer/delegate type. 575 576 Using builtin $(D typeof) on a property function yields the types of the 577 property value, not of the property function itself. Still, 578 $(D FunctionTypeOf) is able to obtain function types of properties. 579 580 Note: 581 Do not confuse function types with function pointer types; function types are 582 usually used for compile-time reflection purposes. 583 */ 584 template FunctionTypeOf(alias func) 585 if (isCallable!func) { 586 static if ((is(typeof(& func) Fsym : Fsym*) && is(Fsym == function)) || is(typeof(& func) Fsym == delegate)) 587 { 588 alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol 589 } 590 else static if (is(typeof(& func.opCall) Fobj == delegate) || is(typeof(& func.opCall!()) Fobj == delegate)) 591 { 592 alias FunctionTypeOf = Fobj; // HIT: callable object 593 } 594 else static if ( 595 (is(typeof(& func.opCall) Ftyp : Ftyp*) && is(Ftyp == function)) || 596 (is(typeof(& func.opCall!()) Ftyp : Ftyp*) && is(Ftyp == function)) 597 ) 598 { 599 alias FunctionTypeOf = Ftyp; // HIT: callable type 600 } 601 else static if (is(func T) || is(typeof(func) T)) 602 { 603 static if (is(T == function)) 604 alias FunctionTypeOf = T; // HIT: function 605 else static if (is(T Fptr : Fptr*) && is(Fptr == function)) 606 alias FunctionTypeOf = Fptr; // HIT: function pointer 607 else static if (is(T Fdlg == delegate)) 608 alias FunctionTypeOf = Fdlg; // HIT: delegate 609 else 610 static assert(0); 611 } 612 else 613 static assert(0); 614 } 615 616 /** 617 Get the type of the return value from a function, 618 a pointer to function, a delegate, a struct 619 with an opCall, a pointer to a struct with an opCall, 620 or a class with an $(D opCall). Please note that $(D_KEYWORD ref) 621 is not part of a type, but the attribute of the function. 622 623 Note: 624 To reduce template instantiations, consider instead using 625 $(D_INLINECODE typeof(() { return func(args); } ())) if the argument types are known or 626 $(D_INLINECODE static if (is(typeof(func) Ret == return))) if only that basic test is needed. 627 */ 628 template ReturnType(alias func) 629 if (isCallable!func) { 630 static if (is(FunctionTypeOf!func R == return)) 631 alias ReturnType = R; 632 else 633 static assert(0, "argument has no return type"); 634 } 635 636 /** 637 Get, as a tuple, the types of the parameters to a function, a pointer 638 to function, a delegate, a struct with an `opCall`, a pointer to a 639 struct with an `opCall`, or a class with an `opCall`. 640 */ 641 template Parameters(alias func) 642 if (isCallable!func) { 643 static if (is(FunctionTypeOf!func P == function)) 644 alias Parameters = P; 645 else 646 static assert(0, "argument has no parameters"); 647 }