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