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