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