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