1 /** 2 * Metaprogramming 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.utils.meta; 15 16 public import ae.utils.meta.reference; 17 public import ae.utils.meta.x; 18 public import ae.utils.meta.proxy; 19 public import ae.utils.meta.binding_v1; 20 public import ae.utils.meta.binding; 21 22 import ae.utils.meta.caps; 23 24 // ************************************************************************ 25 26 import std.traits; 27 28 /** 29 * Same as TypeTuple, but meant to be used with values. 30 * 31 * Example: 32 * foreach (char channel; ValueTuple!('r', 'g', 'b')) 33 * { 34 * // the loop is unrolled at compile-time 35 * // "channel" is a compile-time value, and can be used in string mixins 36 * } 37 */ 38 template ValueTuple(T...) 39 { 40 alias T ValueTuple; 41 } 42 43 template _RangeTupleImpl(size_t N, R...) 44 { 45 static if (N==R.length) 46 alias R _RangeTupleImpl; 47 else 48 alias _RangeTupleImpl!(N, ValueTuple!(R, R.length)) _RangeTupleImpl; 49 } 50 51 /// Generate a tuple containing integers from 0 to N-1. 52 /// Useful for static loop unrolling. (staticIota) 53 template RangeTuple(size_t N) 54 { 55 alias _RangeTupleImpl!(N, ValueTuple!()) RangeTuple; 56 } 57 58 /// Expand an array to a tuple. 59 /// The array value must be known during compilation. 60 template ArrayToTuple(alias arr, Elements...) 61 { 62 /// 63 static if (arr.length) 64 alias ArrayToTuple = ArrayToTuple!(arr[1..$], ValueTuple!(Elements, arr[0])); 65 else 66 alias ArrayToTuple = Elements; 67 } 68 69 unittest 70 { 71 alias X = ArrayToTuple!"abc"; 72 static assert(X[0] == 'a' && X[2] == 'c'); 73 static assert([X] == "abc"); 74 } 75 76 /// Expand a static array to a tuple. 77 /// Unlike `ArrayToTuple`, the array may be a runtime variable. 78 template expand(alias arr, size_t offset = 0) 79 if (isStaticArray!(typeof(arr))) 80 { 81 import std.typetuple : AliasSeq; 82 83 /// 84 static if (arr.length == offset) 85 alias expand = AliasSeq!(); 86 else 87 { 88 @property ref getValue() { return arr[offset]; } 89 alias expand = AliasSeq!(getValue, expand!(arr, offset+1)); 90 } 91 } 92 93 unittest 94 { 95 int[3] arr = [1, 2, 3]; 96 void test(int a, int b, int c) {} 97 test(expand!arr); 98 } 99 100 /// Return something to foreach over optimally. 101 /// If A is known at compile-time, return a tuple, 102 /// so the foreach is unrolled at compile-time. 103 /// Otherwise, return A for a regular runtime foreach. 104 template CTIterate(alias A) 105 { 106 /// 107 static if (is(typeof(ArrayToTuple!A))) 108 enum CTIterate = ArrayToTuple!A; 109 else 110 alias CTIterate = A; 111 } 112 113 unittest 114 { 115 foreach (c; CTIterate!"abc") {} 116 string s; 117 foreach (c; CTIterate!s) {} 118 } 119 120 /// Like std.typecons.Tuple, but a template mixin. 121 /// Unlike std.typecons.Tuple, names may not be omitted - but repeating types may be. 122 /// Example: FieldList!(ubyte, "r", "g", "b", ushort, "a"); 123 mixin template FieldList(Fields...) 124 { 125 mixin(_GenFieldList!(void, Fields)); 126 } 127 128 template _GenFieldList(T, Fields...) 129 { 130 /// 131 static if (Fields.length == 0) 132 enum _GenFieldList = ""; 133 else 134 { 135 static if (is(typeof(Fields[0]) == string)) 136 enum _GenFieldList = T.stringof ~ " " ~ Fields[0] ~ ";\n" ~ _GenFieldList!(T, Fields[1..$]); 137 else 138 enum _GenFieldList = _GenFieldList!(Fields[0], Fields[1..$]); 139 } 140 } 141 142 unittest 143 { 144 struct S 145 { 146 mixin FieldList!(ubyte, "r", "g", "b", ushort, "a"); 147 } 148 S s; 149 static assert(is(typeof(s.r) == ubyte)); 150 static assert(is(typeof(s.g) == ubyte)); 151 static assert(is(typeof(s.b) == ubyte)); 152 static assert(is(typeof(s.a) == ushort)); 153 } 154 155 /// Return true if all of T's fields are the same type. 156 @property bool isHomogeneous(T)() 157 { 158 foreach (i, f; T.init.tupleof) 159 if (!is(typeof(T.init.tupleof[i]) == typeof(T.init.tupleof[0]))) 160 return false; 161 return true; 162 } 163 deprecated alias isHomogenous = isHomogeneous; 164 165 /// Resolves to `true` if tuple `T` contains a value whose type is `X`. 166 template isValueOfTypeInTuple(X, T...) 167 { 168 /// 169 static if (T.length==0) 170 enum bool isValueOfTypeInTuple = false; 171 else 172 static if (T.length==1) 173 enum bool isValueOfTypeInTuple = is(typeof(T[0]) : X); 174 else 175 enum bool isValueOfTypeInTuple = isValueOfTypeInTuple!(X, T[0..$/2]) || isValueOfTypeInTuple!(X, T[$/2..$]); 176 } 177 178 unittest 179 { 180 static assert( isValueOfTypeInTuple!(int, ValueTuple!("a", 42))); 181 static assert(!isValueOfTypeInTuple!(int, ValueTuple!("a", 42.42))); 182 static assert(!isValueOfTypeInTuple!(int, ValueTuple!())); 183 184 static assert(!isValueOfTypeInTuple!(int, "a", int, Object)); 185 static assert( isValueOfTypeInTuple!(int, "a", int, Object, 42)); 186 } 187 188 /// Returns the first value in `T` of type `X`. 189 template findValueOfTypeInTuple(X, T...) 190 { 191 /// 192 static if (T.length==0) 193 static assert(false, "Can't find value of type " ~ X.stringof ~ " in specified tuple"); 194 else 195 static if (is(typeof(T[0]) : X)) 196 enum findValueOfTypeInTuple = T[0]; 197 else 198 enum findValueOfTypeInTuple = findValueOfTypeInTuple!(X, T[1..$]); 199 } 200 201 unittest 202 { 203 static assert(findValueOfTypeInTuple!(int, ValueTuple!("a", 42))==42); 204 static assert(findValueOfTypeInTuple!(int, "a", int, Object, 42)==42); 205 } 206 207 /// Combines the getMember and allMembers traits, to return the 208 /// parameter's members as aliases. 209 template AllMembers(X...) 210 if (X.length == 1) 211 { 212 alias GetMember(string name) = I!(__traits(getMember, X, name)); 213 alias AllMembers = staticMap!(GetMember, __traits(allMembers, X)); 214 } 215 216 unittest 217 { 218 import std.typetuple : AliasSeq; 219 220 struct A { struct B {} struct C {} } 221 static assert(is(AllMembers!A == AliasSeq!(A.B, A.C))); 222 } 223 224 /// One past the biggest element of the enum T. 225 /// Example: string[enumLength!E] arr; 226 template enumLength(T) 227 if (is(T==enum)) 228 { 229 enum enumLength = cast(T)(cast(size_t)T.max + 1); 230 } 231 232 deprecated alias EnumLength = enumLength; 233 234 /// A range that iterates over all members of an enum. 235 @property auto enumIota(T)() 236 { 237 import std.range : iota; 238 return iota(T.init, enumLength!T); 239 } 240 241 unittest 242 { 243 import std.algorithm.comparison : equal; 244 enum E { a, b, c } 245 static assert(equal(enumIota!E, [E.a, E.b, E.c])); 246 } 247 248 // ************************************************************************ 249 250 // Use strong typing to provably disambiguate BoxedVoid from any other type. 251 private struct BoxedVoidElement {} 252 253 /// What to use instead of void for boxVoid/unboxVoid. 254 /// Use a zero-length array instead of an empty struct as this one has a .sizeof 255 /// of 0, unlike the struct. 256 alias BoxedVoid = BoxedVoidElement[0]; 257 258 static assert(BoxedVoid.sizeof == 0); 259 260 /// Resolves to `BoxedVoid` if `T` is `void`, or to `T` otherwise. 261 template BoxVoid(T) 262 { 263 /// 264 static if (is(T == void)) 265 alias BoxVoid = BoxedVoid; 266 else 267 alias BoxVoid = T; 268 } 269 270 /// D does not allow void variables or parameters. 271 /// As such, there is no "common type" for functions that return void 272 /// and non-void. 273 /// To allow generic metaprogramming in such cases, this function will 274 /// "box" a void expression to a different type. 275 BoxVoid!T boxVoid(T)(lazy T expr) 276 { 277 static if (is(T == void)) 278 { 279 expr; 280 return BoxedVoid.init; 281 } 282 else 283 return expr; 284 } 285 286 /// Inverse of boxVoid. 287 /// Can be used in a return statement, i.e.: 288 /// return unboxVoid(someBoxedVoid); 289 auto unboxVoid(T)(T value) 290 { 291 static if (is(T == BoxedVoid)) 292 return; 293 else 294 return value; 295 } 296 297 /// As boxVoid/unboxVoid, but returns a struct with zero or one members. 298 /// .tupleof can then be used to paste the tuple in e.g. an argument list. 299 auto voidStruct(T)(T value) 300 if (!is(T == void)) 301 { 302 static struct Result 303 { 304 static if (!is(T == BoxedVoid)) 305 T value; 306 } 307 308 static if (is(T == BoxedVoid)) 309 return Result(); 310 else 311 return Result(value); 312 } 313 314 /// ditto 315 auto voidStruct(T)(lazy T value) 316 if (is(T == void)) 317 { 318 value; // evaluate 319 320 static struct Result {} 321 return Result(); 322 } 323 324 unittest 325 { 326 import std.typetuple : AliasSeq; 327 328 struct S { void* p; } 329 330 auto process(T)(T delegate() dg) 331 { 332 auto result = dg().boxVoid; 333 return result.unboxVoid; 334 } 335 336 S fun() { return S(); } 337 assert(process(&fun) == S.init); 338 339 void gun() { } 340 static assert(is(typeof(process(&gun)) == void)); 341 342 gun(gun().boxVoid.voidStruct.tupleof); 343 gun(gun().voidStruct.tupleof); 344 static assert(is(typeof(fun().boxVoid.voidStruct.tupleof) == AliasSeq!S)); 345 static assert(is(typeof(fun().voidStruct.tupleof) == AliasSeq!S)); 346 } 347 348 // ************************************************************************ 349 350 /// Apply `.stringof` over `Args` and 351 /// return the result as a `string[]`. 352 static // https://issues.dlang.org/show_bug.cgi?id=7805 353 template stringofArray(Args...) 354 { 355 static string[] stringofArray() 356 { 357 string[] args; 358 foreach (i, _ ; typeof(Args)) 359 args ~= Args[i].stringof; 360 return args; 361 } 362 } 363 364 /// Returns the index of fun's parameter with the name 365 /// matching "names", or asserts if the parameter is not found. 366 /// "names" can contain multiple names separated by slashes. 367 static size_t findParameter(alias fun, string names)() 368 { 369 import std.array : split; 370 371 foreach (name; names.split("/")) 372 foreach (i, param; ParameterIdentifierTuple!fun) 373 if (param == name) 374 return i; 375 assert(false, "Function " ~ __traits(identifier, fun) ~ " doesn't have a parameter called " ~ names); 376 } 377 378 /// ditto 379 // Workaround for no "static alias" template parameters 380 static size_t findParameter()(string[] searchedNames, string soughtNames, string funName) 381 { 382 import std.array : split; 383 384 foreach (soughtName; soughtNames.split("/")) 385 { 386 import std.algorithm.searching : countUntil; 387 388 auto targetIndex = searchedNames.countUntil(soughtName); 389 if (targetIndex >= 0) 390 return targetIndex; 391 } 392 393 { 394 import std.format : format; 395 396 assert(false, "No argument %s in %s's parameters (%s)" 397 .format(soughtNames, funName, searchedNames).idup); 398 } 399 } 400 401 unittest 402 { 403 static void fun(int a, int b, int c) {} 404 405 static assert(findParameter!(fun, "x/c") == 2); 406 assert(findParameter(["a", "b", "c"], "x/c", "fun") == 2); 407 } 408 409 // ************************************************************************ 410 411 /// Generates a function which passes its arguments to a struct, which is 412 /// returned. Preserves field names (as parameter names) and default values. 413 template structFun(S) 414 { 415 mixin((){ 416 import std.algorithm.iteration : map; 417 import std.array : join; 418 import std.format : format; 419 import std.meta : staticMap; 420 import std.range : iota; 421 422 enum identifierAt(int n) = __traits(identifier, S.tupleof[n]); 423 enum names = [staticMap!(identifierAt, RangeTuple!(S.tupleof.length))]; 424 425 return 426 "S structFun(\n" ~ 427 S.tupleof.length.iota.map!(n => 428 " typeof(S.init.tupleof[%d]) %s = S.init.tupleof[%d],\n".format(n, names[n], n) 429 ).join() ~ 430 `) { return S(` ~ names.join(", ") ~ "); }"; 431 }()); 432 } 433 434 unittest 435 { 436 static struct Test 437 { 438 string a; 439 int b = 42; 440 } 441 442 Test test = structFun!Test("banana"); 443 assert(test.a is "banana"); 444 assert(test.b == 42); 445 } 446 447 /// Generates a struct containing fields with names, types, and default values 448 /// corresponding to a function's parameter list. 449 struct StructFromParams(args...) 450 if (args.length == 1 || args.length == 2) 451 { 452 mixin((){ 453 alias fun = args[0]; 454 static if (args.length == 1) 455 enum bool voidInitializeRequired = false; 456 else 457 enum bool voidInitializeRequired = args[1]; 458 import ae.utils.text.ascii : toDec; 459 460 string code; 461 foreach (i; RangeTuple!(ParameterTypeTuple!fun.length)) 462 { 463 enum n = toDec(i); 464 465 code ~= `ParameterTypeTuple!(args[0])[` ~ n ~ `] `; 466 467 static if (ParameterIdentifierTuple!fun[i].length) 468 code ~= ParameterIdentifierTuple!fun[i]; 469 else 470 code ~= "_param_" ~ toDec(i); 471 472 static if (is(ParameterDefaultValueTuple!fun[i] == void)) 473 static if (voidInitializeRequired) 474 code ~= ` = void;`; 475 else 476 code ~= `;`; 477 else 478 code ~= ` = ParameterDefaultValueTuple!(args[0])[` ~ n ~ `];`; 479 } 480 return code; 481 }()); 482 } 483 484 unittest 485 { 486 static void fun(string a, int b = 42) {} 487 alias S = StructFromParams!fun; 488 static assert(is(typeof(S.a) == string)); 489 static assert(S.init.b == 42); 490 } 491 492 unittest 493 { 494 static void fun(string, int = 42) {} 495 alias Fun = typeof(&fun); 496 alias S = StructFromParams!Fun; 497 static assert(is(typeof(S.tupleof[0]) == string)); 498 } 499 500 // ************************************************************************ 501 502 // By Paul Backus: https://forum.dlang.org/post/mkiyylyjznwgkzpnbryk@forum.dlang.org 503 /// Pass struct / tuple members as arguments to a function. 504 alias tupleAs(alias fun) = args => fun(args.tupleof); 505 506 /// Call a predicate with the given value. Return the value. 507 /// Intended to be used in UFCS chains using functions which mutate their argument, 508 /// such as skipOver and each. 509 template apply(alias dg) 510 { 511 auto ref T apply(T)(auto ref T v) 512 { 513 dg(v); 514 return v; 515 } 516 } 517 518 /// 519 unittest 520 { 521 int i = 7; 522 int j = i.apply!((ref v) => v++); 523 assert(j == 8); 524 } 525 526 /// Evaluate all arguments and return the last argument. 527 /// Can be used instead of the comma operator. 528 /// Inspired by http://clhs.lisp.se/Body/s_progn.htm 529 Args[$-1] progn(Args...)(lazy Args args) 530 { 531 foreach (n; RangeTuple!(Args[1..$].length)) 532 cast(void)args[n]; 533 return args[$-1]; 534 } 535 536 unittest 537 { 538 // Test that expressions are correctly evaluated exactly once. 539 int a, b, c, d; 540 d = progn(a++, b++, c++); 541 assert(a==1 && b==1 && c == 1 && d == 0); 542 d = progn(a++, b++, ++c); 543 assert(a==2 && b==2 && c == 2 && d == 2); 544 } 545 546 unittest 547 { 548 // Test void expressions. 549 int a, b; 550 void incA() { a++; } 551 void incB() { b++; } 552 progn(incA(), incB()); 553 assert(a == 1 && b == 1); 554 } 555 556 /// Like progn, but return the first argument instead. 557 Args[0] prog1(Args...)(lazy Args args) 558 { 559 auto result = args[0]; 560 foreach (n; RangeTuple!(Args.length-1)) 561 cast(void)args[1+n]; 562 return result; 563 } 564 565 unittest 566 { 567 int a = 10, b = 20, c = 30; 568 int d = prog1(a++, b++, c++); 569 assert(a==11 && b==21 && c == 31 && d == 10); 570 } 571 572 /// Resolves to `true` if there exists a non-`void` 573 /// common type for all elements of `T`. 574 enum bool haveCommonType(T...) = is(CommonType!T) && !is(CommonType!T == void); 575 576 /// Lazily evaluate and return first true-ish result; otherwise return last result. 577 CommonType!Args or(Args...)(lazy Args args) 578 if (haveCommonType!Args) 579 { 580 foreach (n; RangeTuple!(Args.length-1)) 581 { 582 auto r = args[n]; 583 if (r) 584 return r; 585 } 586 return args[$-1]; 587 } 588 589 unittest 590 { 591 assert(or(0, 7, 5) == 7); 592 assert(or(0, 0, 0) == 0); 593 int fun() { assert(false); } 594 assert(or(0, 7, fun) == 7); 595 } 596 597 /// Lazily evaluate and return first false-ish result; otherwise return last result. 598 CommonType!Args and(Args...)(lazy Args args) 599 if (haveCommonType!Args) 600 { 601 foreach (n; RangeTuple!(Args.length-1)) 602 { 603 auto r = args[n]; 604 if (!r) 605 return r; 606 } 607 return args[$-1]; 608 } 609 610 unittest 611 { 612 assert(and(7, 5, 0) == 0); 613 assert(and(7, 5, 3) == 3); 614 int fun() { assert(false); } 615 assert(and(7, 0, fun) == 0); 616 } 617 618 // ************************************************************************ 619 620 // Using a compiler with UDA support? 621 deprecated alias HAVE_UDA = haveUDA; 622 623 static if (haveUDA) 624 { 625 /* 626 template hasAttribute(T, alias D) 627 { 628 enum bool hasAttribute = isValueOfTypeInTuple!(T, __traits(getAttributes, D)); 629 } 630 */ 631 632 /// Detects types and values of the given type. 633 template hasAttribute(Args...) 634 if (Args.length == 2) 635 { 636 // alias attribute = Args[0]; 637 // alias symbol = Args[1]; 638 639 import std.typetuple : staticIndexOf; 640 import std.traits : staticMap; 641 642 /// 643 static if (is(Args[0])) 644 { 645 template _isTypeOrValueInTuple(T, Args...) 646 { 647 static if (!Args.length) 648 enum _isTypeOrValueInTuple = false; 649 else 650 static if (is(Args[0] == T)) 651 enum _isTypeOrValueInTuple = true; 652 else 653 static if (is(typeof(Args[0]) == T)) 654 enum _isTypeOrValueInTuple = true; 655 else 656 enum _isTypeOrValueInTuple = _isTypeOrValueInTuple!(T, Args[1..$]); 657 } 658 659 enum bool hasAttribute = _isTypeOrValueInTuple!(Args[0], __traits(getAttributes, Args[1])); 660 } 661 else 662 enum bool hasAttribute = staticIndexOf!(Args[0], __traits(getAttributes, Args[1])) != -1; 663 } 664 665 /// Retrieves the attribute (type or value of the given type). 666 template getAttribute(T, alias D) 667 { 668 enum T getAttribute = findValueOfTypeInTuple!(T, __traits(getAttributes, D)); 669 } 670 671 unittest 672 { 673 struct Attr { int i; } 674 675 struct S 676 { 677 @Attr int a; 678 @Attr(5) int b; 679 @("test") int c; 680 } 681 682 static assert(hasAttribute!(Attr, S.a)); 683 static assert(hasAttribute!(Attr, S.b)); 684 static assert(hasAttribute!(string, S.c)); 685 static assert(hasAttribute!("test", S.c)); 686 } 687 } 688 else 689 { 690 /// Stub (unsupported)> 691 template hasAttribute(T, alias D) 692 { 693 enum bool hasAttribute = false; 694 } 695 696 /// ditto 697 template getAttribute(T, alias D) 698 { 699 static assert(false, "This D compiler has no UDA support."); 700 } 701 } 702 703 // ************************************************************************ 704 705 /// Generate constructors that simply call the parent class constructors. 706 /// Based on http://forum.dlang.org/post/i3hpj0$2vc6$1@digitalmars.com 707 mixin template GenerateConstructorProxies() 708 { 709 mixin(() { 710 import std.conv : text; 711 import std.string : join; 712 import std.traits : ParameterTypeTuple, fullyQualifiedName; 713 714 alias T = typeof(super); 715 716 string s; 717 static if (__traits(hasMember, T, "__ctor")) 718 foreach (ctor; __traits(getOverloads, T, "__ctor")) 719 { 720 string[] declarationList, usageList; 721 foreach (i, param; ParameterTypeTuple!(typeof(&ctor))) 722 { 723 auto varName = "v" ~ text(i); 724 declarationList ~= fullyQualifiedName!param ~ " " ~ varName; 725 usageList ~= varName; 726 } 727 s ~= "this(" ~ declarationList.join(", ") ~ ") { super(" ~ usageList.join(", ") ~ "); }\n"; 728 } 729 return s; 730 } ()); 731 } 732 733 deprecated alias GenerateContructorProxies = GenerateConstructorProxies; 734 735 unittest 736 { 737 class A 738 { 739 int i, j; 740 this() { } 741 this(int i) { this.i = i; } 742 this(int i, int j ) { this.i = i; this.j = j; } 743 } 744 745 class B : A 746 { 747 mixin GenerateConstructorProxies; 748 } 749 750 A a; 751 752 a = new B(); 753 assert(a.i == 0); 754 a = new B(17); 755 assert(a.i == 17); 756 a = new B(17, 42); 757 assert(a.j == 42); 758 } 759 760 // ************************************************************************ 761 762 /// Generate a @property function which creates/returns 763 /// a thread-local singleton of a class with the given arguments. 764 765 @property T singleton(T, args...)() 766 if (is(typeof(new T(args)))) 767 { 768 static T instance; 769 if (!instance) 770 instance = new T(args); 771 return instance; 772 } 773 774 unittest 775 { 776 static class C 777 { 778 static int n = 0; 779 780 this() { n++; } 781 this(int x) { n += x; } 782 783 void fun() {} 784 } 785 786 alias singleton!C c0; 787 c0.fun(); 788 c0.fun(); 789 assert(C.n == 1); 790 791 alias singleton!(C, 5) c1; 792 c1.fun(); 793 c1.fun(); 794 assert(C.n == 6); 795 } 796 797 /// As above, but using arbitrary types and a factory function. 798 @property singleton(alias fun, args...)() 799 if (is(typeof(fun(args)))) 800 { 801 alias T = typeof(fun(args)); 802 static T instance; 803 static bool initialized; 804 if (!initialized) 805 { 806 instance = fun(args); 807 initialized = true; 808 } 809 return instance; 810 } 811 812 unittest 813 { 814 int n; 815 int gen(int _ = 0) 816 { 817 return ++n; 818 } 819 820 alias singleton!gen c0; 821 assert(c0 == 1); 822 assert(c0 == 1); 823 824 alias singleton!(gen, 1) c1; 825 assert(c1 == 2); 826 assert(c1 == 2); 827 } 828 829 // ************************************************************************ 830 831 /// Were we built with -debug? 832 debug 833 enum isDebug = true; 834 else 835 enum isDebug = false; 836 837 deprecated alias IsDebug = isDebug; 838 839 /// Is a specific version on? 840 template isVersion(string versionName) 841 { 842 mixin(`version (` ~ versionName ~ `) enum isVersion = true; else enum isVersion = false;`); 843 } 844 845 // ************************************************************************ 846 847 /// Identity function. 848 auto ref T identity(T)(auto ref T value) { return value; } 849 850 /// Shorter synonym for std.traits.Identity. 851 /// Can be used to UFCS-chain static methods and nested functions. 852 alias I(alias A) = A; 853 854 // ************************************************************************ 855 856 /// Get f's ancestor which represents its "this" pointer. 857 /// Skips template and mixin ancestors until it finds a struct or class. 858 template thisOf(alias f) 859 { 860 alias _p = I!(__traits(parent, f)); 861 /// 862 static if (is(_p == class) || is(_p == struct) || is(_p == union)) 863 alias thisOf = _p; 864 else 865 alias thisOf = thisOf!_p; 866 } 867 868 // ************************************************************************ 869 870 /// Return the number of bits used to store the value part, i.e. 871 /// T.sizeof*8 for integer parts and the mantissa size for 872 /// floating-point types. 873 template valueBits(T) 874 { 875 /// 876 static if (is(T : ulong)) 877 enum valueBits = T.sizeof * 8; 878 else 879 static if (is(T : real)) 880 enum valueBits = T.mant_dig; 881 else 882 static assert(false, "Don't know how many value bits there are in " ~ T.stringof); 883 } 884 885 static assert(valueBits!uint == 32); 886 static assert(valueBits!double == 53); 887 888 /// Expand to a built-in numeric type of the same kind 889 /// (signed integer / unsigned integer / floating-point) 890 /// with at least the indicated number of bits of precision. 891 template ResizeNumericType(T, uint bits) 892 { 893 /// 894 static if (is(T : ulong)) 895 static if (isSigned!T) 896 alias ResizeNumericType = SignedBitsType!bits; 897 else 898 alias ResizeNumericType = UnsignedBitsType!bits; 899 else 900 static if (is(T : real)) 901 { 902 static if (bits <= float.mant_dig) 903 alias ResizeNumericType = float; 904 else 905 static if (bits <= double.mant_dig) 906 alias ResizeNumericType = double; 907 else 908 static if (bits <= real.mant_dig) 909 alias ResizeNumericType = real; 910 else 911 static assert(0, "No floating-point type big enough to fit " ~ bits.stringof ~ " bits"); 912 } 913 else 914 static assert(false, "Don't know how to resize type: " ~ T.stringof); 915 } 916 917 static assert(is(ResizeNumericType!(float, double.mant_dig) == double)); 918 919 /// Expand to a built-in numeric type of the same kind 920 /// (signed integer / unsigned integer / floating-point) 921 /// with at least additionalBits more bits of precision. 922 alias ExpandNumericType(T, uint additionalBits) = 923 ResizeNumericType!(T, valueBits!T + additionalBits); 924 925 /// Like ExpandNumericType, but do not error if the resulting type is 926 /// too large to fit any native D type - just expand to the largest 927 /// type of the same kind instead. 928 template TryExpandNumericType(T, uint additionalBits) 929 { 930 /// 931 static if (is(typeof(ExpandNumericType!(T, additionalBits)))) 932 alias TryExpandNumericType = ExpandNumericType!(T, additionalBits); 933 else 934 static if (is(T : ulong)) 935 static if (isSigned!T) 936 alias TryExpandNumericType = long; 937 else 938 alias TryExpandNumericType = ulong; 939 else 940 static if (is(T : real)) 941 alias TryExpandNumericType = real; 942 else 943 static assert(false, "Don't know how to expand type: " ~ T.stringof); 944 } 945 946 /// Integer type big enough to fit N bits of precision. 947 template UnsignedBitsType(uint bits) 948 { 949 /// 950 static if (bits <= 8) 951 alias ubyte UnsignedBitsType; 952 else 953 static if (bits <= 16) 954 alias ushort UnsignedBitsType; 955 else 956 static if (bits <= 32) 957 alias uint UnsignedBitsType; 958 else 959 static if (bits <= 64) 960 alias ulong UnsignedBitsType; 961 else 962 static assert(0, "No integer type big enough to fit " ~ bits.stringof ~ " bits"); 963 } 964 965 /// ditto 966 template SignedBitsType(uint bits) 967 { 968 alias Signed!(UnsignedBitsType!bits) SignedBitsType; 969 } 970 971 /// Evaluates to array of strings with name for each field. 972 @property string[] structFields(T)() 973 if (is(T == struct) || is(T == class)) 974 { 975 import std.string : split; 976 977 string[] fields; 978 foreach (i, f; T.init.tupleof) 979 { 980 string field = T.tupleof[i].stringof; 981 field = field.split(".")[$-1]; 982 fields ~= field; 983 } 984 return fields; 985 } 986 987 /// Returns the class's initializer instance. 988 /// Returns null if all class fields are zero. 989 /// Can be used to get the value of class fields' initial values. 990 immutable(T) classInit(T)() 991 if (is(T == class)) 992 { 993 return cast(immutable(T))typeid(T).initializer.ptr; 994 } 995 996 /// 997 unittest 998 { 999 class C { int n = 42; } 1000 assert(classInit!C.n == 42); 1001 } 1002 1003 /// Create a functor value type (bound struct) from an alias. 1004 template functor(alias fun) 1005 { 1006 struct Functor 1007 { 1008 //alias opCall = fun; 1009 auto opCall(T...)(auto ref T args) { return fun(args); } 1010 } 1011 1012 Functor functor() 1013 { 1014 Functor f; 1015 return f; 1016 } 1017 } 1018 1019 static if (haveAliasStructBinding) 1020 unittest 1021 { 1022 static void caller(F)(F fun) 1023 { 1024 fun(42); 1025 } 1026 1027 int result; 1028 caller(functor!((int i) => result = i)); 1029 assert(result == 42); 1030 }