1 /// Various utilities for hacking the D type system 2 module mecca.lib.reflection; 3 4 // Licensed under the Boost license. Full copyright information in the AUTHORS file 5 6 import std.algorithm: move, moveEmplace, any; 7 public import std.traits; 8 public import std.meta; 9 import std.conv; 10 import std.stdint: intptr_t; 11 12 // Disable tracing instrumentation for the whole file 13 @("notrace") void traceDisableCompileTimeInstrumentation(); 14 15 /// Return the smallest type large enough to hold the numbers 0..n 16 template CapacityType(size_t n) { 17 static if (n <= ubyte.max) { 18 alias CapacityType = ubyte; 19 } 20 else static if (n <= ushort.max) { 21 alias CapacityType = ushort; 22 } 23 else static if (n <= uint.max) { 24 alias CapacityType = uint; 25 } 26 else { 27 alias CapacityType = ulong; 28 } 29 } 30 31 unittest { 32 static assert (is(CapacityType!17 == ubyte)); 33 static assert (is(CapacityType!17_000 == ushort)); 34 static assert (is(CapacityType!17_000_000 == uint)); 35 static assert (is(CapacityType!17_000_000_000 == ulong)); 36 } 37 38 /// Type, unless it is smaller than int, in which case it is promoted 39 /// 40 /// This is useful for predicting the return type due to algebraic type promotion 41 template PromotedType(Type) { 42 alias PromotedType = typeof(Type(0)+Type(0)); 43 } 44 45 unittest { 46 static assert( is( PromotedType!ubyte == int ) ); 47 static assert( is( PromotedType!byte == int ) ); 48 static assert( is( PromotedType!ushort == int ) ); 49 static assert( is( PromotedType!short == int ) ); 50 static assert( is( PromotedType!int == int ) ); 51 static assert( is( PromotedType!uint == uint ) ); 52 static assert( is( PromotedType!long == long ) ); 53 static assert( is( PromotedType!ulong == ulong ) ); 54 } 55 56 private enum badStorageClass(uint cls) = (cls != ParameterStorageClass.none); 57 58 /** 59 * Non-GC postponed function call with arguments (closure) 60 */ 61 struct Closure { 62 enum ARGS_SIZE = 128; 63 64 private: 65 enum DIRECT_FN = cast(void function(Closure*))0x1; 66 enum DIRECT_DG = cast(void function(Closure*))0x2; 67 68 void function(Closure*) _wrapper; 69 void* _funcptr; 70 union { 71 void delegate() _dg; 72 ubyte[ARGS_SIZE] argsBuf; 73 } 74 75 public: 76 @property const(void)* funcptr() pure const nothrow @nogc { 77 return _funcptr; 78 } 79 @property bool isSet() pure const nothrow @nogc { 80 return _funcptr !is null; 81 } 82 bool opCast(T: bool)() const pure @nogc nothrow @safe { 83 return _funcptr !is null; 84 } 85 ref auto opAssign(typeof(null)) pure @nogc nothrow @safe { 86 clear(); 87 return this; 88 } 89 ref auto opAssign(void function() fn) pure @nogc nothrow @safe { 90 set(fn); 91 return this; 92 } 93 ref auto opAssign(void delegate() dg) pure @nogc nothrow @safe { 94 set(dg); 95 return this; 96 } 97 98 void clear() pure nothrow @nogc @safe { 99 _wrapper = null; 100 _funcptr = null; 101 argsBuf[] = 0; 102 } 103 104 void set(F, T...)(F f, T args) nothrow @nogc @trusted if (isFunctionPointer!F) { 105 static assert (is(ReturnType!F == void), "Delegate must return void"); 106 static assert (is(typeof(f(args))), "Args don't match passed delegate"); 107 108 alias PSCT = ParameterStorageClassTuple!F; 109 foreach( i; IOTA!(T.length) ) { 110 import std..string: format; 111 112 static assert( 113 PSCT[i] != ParameterStorageClass.ref_, 114 "Closure cannot be used over functions with a ref variables (argument %s)".format(i+1) ); 115 static assert( 116 PSCT[i] != ParameterStorageClass.out_, 117 "Closure cannot be used over functions with an out variables (argument %s)".format(i+1) ); 118 static assert( 119 PSCT[i] != ParameterStorageClass.lazy_, 120 "Closure cannot be used over functions with a lazy variables (argument %s)".format(i+1) ); 121 } 122 123 static if (T.length == 0) { 124 _wrapper = DIRECT_FN; 125 _funcptr = f; 126 argsBuf[] = 0; 127 } 128 else { 129 struct Typed { 130 T args; 131 } 132 static assert (Typed.sizeof <= argsBuf.sizeof, "Args too big"); 133 134 static void wrapper(Closure* closure) { 135 mixin( genMoveArgument( 136 T.length, 137 "(cast(F)closure._funcptr)", 138 "(cast(Typed*)closure.argsBuf.ptr).args") ); 139 } 140 _funcptr = f; 141 _wrapper = &wrapper; 142 foreach( i, ref arg; args ) 143 moveEmplace( arg, (cast(Typed*)argsBuf.ptr).args[i] ); 144 argsBuf[Typed.sizeof .. $] = 0; 145 } 146 } 147 148 void set(D, T...)(D dg, T args) pure nothrow @nogc @trusted if (isDelegate!D) { 149 static assert (is(ReturnType!D == void), "Delegate must return void"); 150 static assert (is(typeof(dg(args))), "Args don't match passed delegate"); 151 152 alias PSCT = ParameterStorageClassTuple!D; 153 foreach( i; IOTA!(T.length) ) { 154 import std..string: format; 155 156 static assert( 157 PSCT[i] != ParameterStorageClass.ref_, 158 "Closure cannot be used over functions with a ref variables (argument %s)".format(i+1) ); 159 static assert( 160 PSCT[i] != ParameterStorageClass.out_, 161 "Closure cannot be used over functions with an out variables (argument %s)".format(i+1) ); 162 static assert( 163 PSCT[i] != ParameterStorageClass.lazy_, 164 "Closure cannot be used over functions with a lazy variables (argument %s)".format(i+1) ); 165 } 166 167 static if (T.length == 0) { 168 _wrapper = DIRECT_DG; 169 _funcptr = dg.funcptr; 170 _dg = dg; 171 argsBuf[_dg.sizeof .. $] = 0; 172 } 173 else { 174 struct Typed { 175 D dg; 176 T args; 177 } 178 import std..string: format; 179 static assert(Typed.sizeof <= ARGS_SIZE, "Arguments to function need %s bytes, can only store up to %s". 180 format(Typed.sizeof, ARGS_SIZE) ); 181 182 static void wrapper(Closure* closure) { 183 auto typed = cast(Typed*)closure.argsBuf.ptr; 184 mixin( genMoveArgument(args.length, "typed.dg", "typed.args") ); 185 } 186 _wrapper = &wrapper; 187 _funcptr = dg.funcptr; 188 auto typed = cast(Typed*)argsBuf.ptr; 189 typed.dg = dg; 190 foreach(i, ref arg; args) { 191 moveEmplace( arg, typed.args[i] ); 192 } 193 argsBuf[Typed.sizeof .. $] = 0; 194 } 195 } 196 197 void set(alias F)(Parameters!F args) nothrow @nogc @trusted 198 if (!isType!F) { 199 static assert (is(ReturnType!F == void), "Delegate must return void"); 200 foreach(i, storage; ParameterStorageClassTuple!F) { 201 static assert( 202 !badStorageClass!storage, 203 "Argument " ~ text(i) ~ " has non-plain storage class " ~ 204 bitEnumToString!ParameterStorageClass(storage) ); 205 } 206 207 alias PSCT = ParameterStorageClassTuple!F; 208 foreach( i, sc; PSCT ) { 209 import std..string: format; 210 211 static assert( 212 sc != ParameterStorageClass.ref_, 213 "Closure cannot be used over functions with a ref variables (argument %s)".format(i+1) ); 214 static assert( 215 sc != ParameterStorageClass.out_, 216 "Closure cannot be used over functions with an out variables (argument %s)".format(i+1) ); 217 static assert( 218 sc != ParameterStorageClass.lazy_, 219 "Closure cannot be used over functions with a lazy variables (argument %s)".format(i+1) ); 220 } 221 222 static if (Parameters!F.length == 0) { 223 _wrapper = DIRECT_FN; 224 _funcptr = &F; 225 argsBuf[] = 0; 226 } 227 else { 228 struct Typed { 229 staticMap!(Unqual, Parameters!F) args; 230 } 231 import std..string: format; 232 static assert(Typed.sizeof <= ARGS_SIZE, "Arguments to function need %s bytes, can only store up to %s". 233 format(Typed.sizeof, ARGS_SIZE) ); 234 static void wrapper(Closure* closure) { 235 Typed* typed = cast(Typed*)closure.argsBuf.ptr; 236 mixin( genMoveArgument(args.length, "F", "typed.args") ); 237 } 238 239 _wrapper = &wrapper; 240 _funcptr = &F; 241 foreach(i, ref arg; args ) { 242 moveEmplace( arg, (cast(Typed*)argsBuf.ptr).args[i] ); 243 } 244 argsBuf[Typed.sizeof .. $] = 0; 245 } 246 } 247 248 void opCall() { 249 if (_funcptr is null) { 250 return; 251 } 252 else if (_wrapper == DIRECT_FN) { 253 (cast(void function())_funcptr)(); 254 } 255 else if (_wrapper == DIRECT_DG) { 256 _dg(); 257 } 258 else { 259 (cast(void function(Closure*))_wrapper)(&this); 260 } 261 } 262 263 } 264 265 unittest { 266 static long sum; 267 268 static void f(int x) { 269 sum += 1000 * x; 270 } 271 272 struct S { 273 int z; 274 void g(int x, int y) { 275 sum += 1_000_000 * x + y * 100 + z; 276 } 277 } 278 279 Closure c; 280 assert (!c); 281 282 c.set(&f, 5); 283 assert (c); 284 285 c(); 286 assert (c.funcptr == &f); 287 assert (sum == 5_000); 288 289 S s; 290 s.z = 99; 291 c.set(&s.g, 8, 18); 292 c(); 293 assert (c.funcptr == (&s.g).funcptr); 294 assert (sum == 5_000 + 8_000_000 + 1_800 + 99); 295 296 c = null; 297 //c(); 298 assert (!c); 299 300 import std.functional: toDelegate; 301 302 sum = 0; 303 c.set(toDelegate(&f), 50); 304 c(); 305 assert (sum == 50_000); 306 307 static void h(int x, double y, string z) { 308 sum = cast(long)(x * y) + z.length; 309 } 310 311 sum = 0; 312 c.set!h(16, 8.5, "hello"); 313 c(); 314 assert (sum == cast(long)(16 * 8.5 + 5)); 315 } 316 317 unittest { 318 Closure c; 319 320 void func(lazy int a) { 321 } 322 323 int var; 324 325 // This check depends on an advanced enough version of the compiler 326 static assert( !__traits(compiles, c.set(&func, var)) ); 327 } 328 329 void setToInit(bool allowDestructors = false, T)(ref T val) nothrow @trusted @nogc if (!isPointer!T) { 330 auto initBuf = cast(ubyte[])typeid(T).initializer(); 331 if (initBuf.ptr is null) { 332 val.asBytes!allowDestructors[] = 0; 333 } 334 else { 335 // duplicate static arrays to work around https://issues.dlang.org/show_bug.cgi?id=16394 336 static if (isStaticArray!T) { 337 foreach(ref e; val) { 338 e.asBytes!allowDestructors[] = initBuf; 339 } 340 } 341 else { 342 val.asBytes!allowDestructors[] = initBuf; 343 } 344 } 345 } 346 347 void setToInit(bool allowDestructors = false, T)(T* val) nothrow @nogc if (!isPointer!T) { 348 pragma(inline, true); 349 setToInit!allowDestructors(*val); 350 } 351 352 void copyTo(T)(const ref T src, ref T dst) nothrow @nogc { 353 pragma(inline, true); 354 (cast(ubyte*)&dst)[0 .. T.sizeof] = (cast(ubyte*)&src)[0 .. T.sizeof]; 355 } 356 357 unittest { 358 struct S { 359 int x = 5; 360 int y = 7; 361 int* p; 362 } 363 364 { 365 S s = S(1,2,null); 366 setToInit(s); 367 assert(S.init == s); 368 } 369 370 { 371 int y; 372 S s = S(1,2,&y); 373 setToInit(&s); 374 assert(S.init == s); 375 } 376 377 { 378 S s = S(1,2,null); 379 auto p = &s; 380 setToInit(p); 381 assert(S.init == s); 382 } 383 384 { 385 // no initializer 386 int x = 5; 387 setToInit(x); 388 assert(x == 0); 389 setToInit(&x); 390 assert(x == 0); 391 } 392 393 { 394 // static array & no initializer 395 int[2] x = [1, 2]; 396 setToInit(x); 397 import std.algorithm : equal; 398 assert(x[].equal([0,0])); 399 x = [3, 4]; 400 setToInit(&x); 401 assert(x[].equal([0,0])); 402 } 403 404 { 405 // static array & initializer 406 S[2] x = [S(1,2), S(3,4)]; 407 assert(x[0].x == 1 && x[0].y == 2); 408 assert(x[1].x == 3 && x[1].y == 4); 409 setToInit(x); 410 import std.algorithm : equal; 411 assert(x[].equal([S.init, S.init])); 412 x = [S(0,0),S(0,0)]; 413 setToInit(&x); 414 assert(x[].equal([S.init, S.init])); 415 } 416 } 417 418 ubyte[] asBytes(bool disableDestructorsAssert = false, T)(const ref T val) nothrow @nogc if (!isPointer!T) { 419 pragma(inline, true); 420 static assert( disableDestructorsAssert || !hasElaborateDestructor!T, 421 "Cannot convert to bytes a type with destructor" ); 422 return (cast(ubyte*)&val)[0 .. T.sizeof]; 423 } 424 425 unittest { 426 static struct S { 427 int a = 999; 428 } 429 430 S s; 431 s.a = 111; 432 setToInit(s); 433 assert (s.a == 999); 434 435 S[17] arr; 436 foreach(ref s2; arr) { 437 s2.a = 111; 438 } 439 assert (arr[0].a == 111 && arr[$-1].a == 111); 440 setToInit(arr); 441 assert (arr[0].a == 999 && arr[$-1].a == 999); 442 } 443 444 template staticIota(int beg, int end) 445 { 446 static if (beg + 1 >= end) 447 { 448 static if (beg >= end) 449 { 450 alias staticIota = AliasSeq!(); 451 } 452 else 453 { 454 alias staticIota = AliasSeq!(+beg); 455 } 456 } 457 else 458 { 459 enum mid = beg + (end - beg) / 2; 460 alias staticIota = AliasSeq!(staticIota!(beg, mid), staticIota!(mid, end)); 461 } 462 } 463 464 465 alias IOTA(size_t end) = staticIota!(0, end); 466 467 unittest { 468 foreach(i; IOTA!10) { 469 enum x = i; 470 } 471 } 472 473 FunctionAttribute toFuncAttrs(string attrs) { 474 import std.algorithm.iteration : splitter, fold, map; 475 return attrs.splitter(" ").map!( 476 (word){ 477 if(word == "@nogc" || word == "nogc") return FunctionAttribute.nogc; 478 if(word == "nothrow") return FunctionAttribute.nothrow_; 479 if(word == "pure") return FunctionAttribute.pure_; 480 if(word == "@safe" || word == "safe") return FunctionAttribute.safe; 481 assert(false, word ~ " is not a valid attribute: must be @nogc, @safe, nothrow or pure"); 482 }).fold!((a, b) => a|b)(FunctionAttribute.none); 483 } 484 485 /// Return a `FunctionAttribute` converted to a mixinable string 486 string funcAttrToString(FunctionAttribute attr) { 487 string ret; 488 string space; 489 490 if( attr & FunctionAttribute.pure_ ) { 491 ret ~= "pure"; 492 space = " "; 493 } 494 if( attr & FunctionAttribute.nothrow_ ) { 495 ret ~= "nothrow" ~ space; 496 space = " "; 497 } 498 if( attr & FunctionAttribute.property ) { 499 ret ~= "@property" ~ space; 500 space = " "; 501 } 502 if( attr & FunctionAttribute.trusted ) { 503 ret ~= "@trusted" ~ space; 504 space = " "; 505 } 506 if( attr & FunctionAttribute.safe ) { 507 ret ~= "@safe" ~ space; 508 space = " "; 509 } 510 if( attr & FunctionAttribute.nogc ) { 511 ret ~= "@nogc" ~ space; 512 space = " "; 513 } 514 if( attr & FunctionAttribute.system ) { 515 ret ~= "@system" ~ space; 516 space = " "; 517 } 518 if( attr & FunctionAttribute.const_ ) { 519 ret ~= "const" ~ space; 520 space = " "; 521 } 522 if( attr & FunctionAttribute.immutable_ ) { 523 ret ~= "immutable" ~ space; 524 space = " "; 525 } 526 if( attr & FunctionAttribute.inout_ ) { 527 ret ~= "inout" ~ space; 528 space = " "; 529 } 530 if( attr & FunctionAttribute.shared_ ) { 531 ret ~= "shared" ~ space; 532 space = " "; 533 } 534 535 return ret; 536 } 537 538 /// Execute a given piece of code while casting it: 539 auto ref as(string attrs, Func)(scope Func func) if (isFunctionPointer!Func || isDelegate!Func) { 540 pragma(inline, true); 541 return (cast(SetFunctionAttributes!(Func, functionLinkage!Func, functionAttributes!Func | toFuncAttrs(attrs)))func)(); 542 } 543 544 unittest { 545 static ubyte[] f() @nogc { 546 //new ubyte[100]; 547 return as!"@nogc"({return new ubyte[100];}); 548 } 549 static void g() nothrow { 550 //throw new Exception("FUUU"); 551 as!"nothrow"({throw new Exception("FUUU");}); 552 } 553 /+static void h() pure { 554 static int x; 555 as!"pure"({x++;}); 556 }+/ 557 /+static void k() @safe { 558 int y; 559 void* x = &y; 560 //*(cast(int*)x) = 5; 561 as!"@safe"({*(cast(int*)x) = 5;}); 562 }+/ 563 } 564 565 template isVersion(string NAME) { 566 mixin("version ("~ NAME ~") {enum isVersion = true;} else {enum isVersion=false;}"); 567 } 568 569 unittest { 570 static assert(isVersion!"unittest"); 571 static assert(isVersion!"assert"); 572 static assert(!isVersion!"slklfjsdkjfslk234r32c"); 573 } 574 575 debug { 576 enum isDebug = true; 577 } 578 else { 579 enum isDebug = false; 580 } 581 582 private string signatureOf(Ts...)() if (Ts.length == 1) { 583 import std.conv; 584 585 alias T = Ts[0]; 586 static if (isSomeFunction!T) { 587 string s = "("; 588 foreach(i, U; ParameterTypeTuple!T) { 589 s ~= signatureOf!U ~ " " ~ int(ParameterStorageClassTuple!T[i]).to!string() ~ ParameterIdentifierTuple!T[i] ~ ","; 590 } 591 return s ~ ")->" ~ signatureOf!(ReturnType!T); 592 } 593 else static if (is(T == struct) || is(T == union)) { 594 string s = T.stringof ~ "{"; 595 foreach(i, U; typeof(T.tupleof)) { 596 s ~= signatureOf!U ~ " " ~ __traits(identifier, T.tupleof[i]); 597 static if (is(T == struct)) { 598 s ~= ","; 599 } 600 else { 601 s ~= "|"; 602 } 603 } 604 return s ~ "}"; 605 } 606 else static if (isBuiltinType!T) { 607 return T.stringof; 608 } 609 else static if (is(T == U*, U)) { 610 return U.stringof ~ "*"; 611 } 612 else { 613 static assert (false, T.stringof); 614 } 615 } 616 617 ulong abiSignatureOf(Ts...)() if (Ts.length == 1) { 618 import mecca.lib.hashing: murmurHash3_64; 619 return murmurHash3_64(signatureOf!(Ts)); 620 } 621 622 version (unittest) { 623 static struct WWW { 624 int x, y; 625 } 626 627 static struct SSS { 628 int a; 629 long b; 630 double c; 631 SSS* d; 632 WWW[2] e; 633 string f; 634 } 635 636 static int fxxx(int x, SSS s, const ref SSS t) { 637 return 7; 638 } 639 } 640 641 unittest { 642 import std.conv; 643 644 enum sig = signatureOf!fxxx; 645 646 enum expected = "(int 0x,SSS{int a,long b,double c,SSS* d,WWW[2] e,string f,} 0s,const(SSS){const(int) a," ~ 647 "const(long) b,const(double) c,const(SSS)* d,const(WWW[2]) e,const(string) f,} 4t,)->int"; 648 static assert (sig == expected, "\n" ~ sig ~ "\n" ~ expected); 649 enum hsig = abiSignatureOf!fxxx; 650 651 static assert (hsig == 2694514427802277758LU, text(hsig)); 652 } 653 654 template callableMembersOf(T) { 655 import std.typetuple: Filter; 656 template isCallableMember(string memberName) { 657 enum isCallableMember = isCallable!(__traits(getMember, T, memberName)); 658 } 659 enum callableMembersOf = Filter!(isCallableMember, __traits(allMembers, T)); 660 } 661 662 struct StructImplementation(I) { 663 mixin((){ 664 string s; 665 foreach (name; callableMembersOf!I) { 666 s ~= "ReturnType!(__traits(getMember, I, \"" ~ name ~ "\")) delegate(ParameterTypeTuple!(__traits(getMember, I, \"" ~ name ~ "\")) args) " ~ name ~ ";\n"; 667 } 668 return s; 669 }()); 670 671 this(T)(ref T obj) { 672 opAssign(obj); 673 } 674 this(T)(T* obj) { 675 opAssign(obj); 676 } 677 678 ref auto opAssign(typeof(null) _) { 679 foreach(ref field; this.tupleof) { 680 field = null; 681 } 682 return this; 683 } 684 ref auto opAssign(const ref StructImplementation impl) { 685 this.tupleof = impl.tupleof; 686 return this; 687 } 688 ref auto opAssign(T)(T* obj) { 689 return obj ? opAssign(*obj) : opAssign(null); 690 } 691 ref auto opAssign(T)(ref T obj) { 692 foreach(name; callableMembersOf!I) { 693 __traits(getMember, this, name) = &__traits(getMember, obj, name); 694 } 695 return this; 696 } 697 698 @property bool isValid() { 699 // enough to check one member - all are assigned at the same time 700 return this.tupleof[0] !is null; 701 } 702 } 703 704 unittest { 705 interface Foo { 706 int func1(string a, double b); 707 void func2(int c); 708 } 709 710 struct MyStruct { 711 int func1(string a, double b) { 712 return cast(int)(a.length * b); 713 } 714 void func2(int c) { 715 } 716 void func2() { 717 } 718 } 719 720 StructImplementation!Foo simpl; 721 assert (!simpl.isValid); 722 MyStruct ms; 723 simpl = ms; 724 assert (simpl.isValid); 725 726 assert (simpl.func1("hello", 3.001) == 15); 727 728 simpl = null; 729 assert (!simpl.isValid); 730 } 731 732 alias ParentType(alias METHOD) = Alias!(__traits(parent, METHOD)); 733 734 auto methodCall(alias METHOD)(ParentType!METHOD* instance, ParameterTypeTuple!METHOD args) if(is(ParentType!METHOD == struct)) { 735 return __traits(getMember, instance, __traits(identifier, METHOD))(args); 736 } 737 auto methodCall(alias METHOD)(ParentType!METHOD instance, ParameterTypeTuple!METHOD args) if(!is(ParentType!METHOD == struct)) { 738 return __traits(getMember, instance, __traits(identifier, METHOD))(args); 739 } 740 741 unittest { 742 struct MyStruct { 743 int x; 744 int f() { 745 return x * 2; 746 } 747 } 748 749 auto ms = MyStruct(17); 750 assert (ms.f() == 34); 751 assert (methodCall!(MyStruct.f)(&ms) == 34); 752 } 753 754 template StaticRegex(string exp, string flags = "") { 755 import std.regex; 756 __gshared static Regex!char StaticRegex; 757 shared static this() { 758 StaticRegex = regex(exp, flags); 759 } 760 } 761 762 /// Replace std.traits.ForeachType with one that works when the item is copyable or not 763 template ForeachTypeof (T) { 764 alias TU = Unqual!T; 765 static if(__traits(compiles, {foreach(x; TU.init) {}})) { 766 alias ForeachTypeof = ReturnType!({ foreach(x;TU.init) { return x; } assert(0); }); 767 } else { 768 alias ForeachTypeof = PointerTarget!(ReturnType!({ foreach(ref x;TU.init) { return &x; } assert(0); })); 769 } 770 } 771 772 unittest { 773 struct A{ @disable this(this); } 774 struct B{ int opApply(int delegate(int x) dlg) { return 0; } } 775 alias AF = ForeachTypeof!(A[]); 776 alias BF = ForeachTypeof!B; 777 alias IF = ForeachTypeof!(int[5]); 778 } 779 780 /** 781 * A CTFE function for generating a mixin string of a function call moving arguments 782 * 783 * Params: 784 * numArgs = number of arguments in generated function call 785 * callString = the string used to call the function 786 * argumentString = the string used to specify a specific argument 787 * ret = optional variable to receive the function's return 788 */ 789 string genMoveArgument(size_t numArgs, string callString, string argumentString, string ret = null) @safe pure { 790 import std..string: format; 791 792 string retVal; 793 if( ret !is null ) 794 retVal ~= "%s = ".format(ret); 795 retVal ~= "%s( ".format(callString); 796 foreach(i; 0..numArgs) { 797 if( i>0 ) 798 retVal ~= ", "; 799 retVal ~= "move(%s[%s])".format(argumentString, i); 800 } 801 802 retVal ~= " );"; 803 804 return retVal; 805 } 806 807 /+ 808 /** 809 * Function for selecting a specific overload of a function 810 * 811 * Params: 812 * name = The name of the function to get 813 * Type = The requested type of the result 814 * instance = The instance to which the delegate should refer 815 * 816 * Returns: 817 * The delegate for calling the specific overload over the specific instance. 818 */ 819 Type getOverload(string name, Type, T)(ref T instance) { 820 pragma(msg, "getOverload ", T.stringof, ".", name, " of type ", Type.stringof); 821 foreach(overload; __traits(getOverloads, instance, name)) { 822 pragma(msg, typeof(overload)); 823 static if( isImplicitlyConvertible!( typeof(overload), Type ) ) { 824 return &overload; 825 enum MatchFound = true; 826 } 827 } 828 static assert( __traits(compiles, MatchFound), "Non of the overloads matched" ); 829 } 830 831 XXX Disabled due to compiler bug in __traits(parent) 832 833 template getOverload(alias F, Args...) { 834 bool predicate(Func)() { 835 static if( Parameters!Func == Args ) { 836 return true; 837 } else { 838 return false; 839 } 840 } 841 842 pragma(msg, "id ", __traits(identifier, F)); 843 pragma(msg, "type ", fullyQualifiedName!F, " = ", typeof(F)); 844 pragma(msg, "parent ", fullyQualifiedName!(__traits(parent, F)), " = ", typeof(__traits(parent, F))); 845 pragma(msg, "parent 2 ", fullyQualifiedName!(__traits(parent, __traits(parent, F))), " = ", typeof(__traits(parent, __traits(parent, F)))); 846 pragma(msg, __traits(getOverloads, __traits(parent, F), __traits(identifier, F)) ); 847 /+ 848 alias getOverload = Filter!("predicate", 849 __traits(getOverloads, __traits(parent, F), __traits(identifier, F))); 850 +/ 851 alias getOverload = void; 852 pragma(msg, typeof(getOverload)); 853 } 854 +/ 855 856 /// CTFE template for genering a string for mixin copying a function's signature along with its default values 857 /// 858 /// Bugs: 859 /// Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=18572, issue 18572) this will not copy extended 860 /// attributes of the arguments (ref, out etc). All arguments are passed by value. 861 /// 862 /// The generated code refers to `srcFunc` by its fully qualified name. Unfortunately, this means we cannot apply to 863 /// functions nested inside other functions. 864 template CopySignature(alias srcFunc, int argumentsBegin = 0, int argumentsEnd = Parameters!srcFunc.length) { 865 private alias Args = Parameters!srcFunc; 866 private alias Defaults = ParameterDefaults!srcFunc; 867 // TODO fullyQualifiedName returns an unusable identifier in case of a nested function 868 private enum funcName = fullyQualifiedName!srcFunc; 869 870 import std.format : format; 871 872 /// Generates a definition list. 873 /// 874 /// Note: 875 /// The arguments are going to be named "arg0" through "argN". The first argument will be `arg0` even if 876 /// `argumentsBegin` is not 0. 877 string genDefinitionList() pure @safe { 878 string ret; 879 880 foreach(i, type; Defaults[argumentsBegin..argumentsEnd]) { 881 if( i>0 ) 882 ret ~= ", "; 883 884 // DMDBUG: we need to give range if we want to maintain ref/out attributes (Params[0..1] arg0). 885 // Due to issue 18572, however, we would then not be able to provide default arguments. 886 ret ~= "Parameters!(%s)[%s] arg%s".format(funcName, i+argumentsBegin, i); 887 888 static if( !is(type == void) ) { 889 ret ~= " = ParameterDefaults!(%s)[%s]".format(funcName, i+argumentsBegin); 890 } 891 } 892 893 return ret; 894 } 895 896 /// Generate a calling list. Simply the list of arg0 through argN separated by commas 897 string genCallList() pure @safe { 898 string ret; 899 900 foreach(i; 0..argumentsEnd-argumentsBegin) { 901 if( i>0 ) 902 ret ~= ", "; 903 904 ret ~= "arg%s".format(i); 905 } 906 907 return ret; 908 } 909 } 910 911 version(unittest) { 912 private int UTfunc1( int a, int b = 3 ) { 913 return a += b; 914 } 915 } 916 917 unittest { 918 import std.format : format; 919 920 alias CopySig = CopySignature!UTfunc1; 921 922 enum MixIn = q{ 923 int func2( %s ) { 924 return UTfunc1( %s ); 925 } 926 }.format( CopySig.genDefinitionList, CopySig.genCallList ); 927 // pragma(msg, MixIn); 928 mixin(MixIn); 929 930 int a=2; 931 assert( func2(a)==5 ); 932 assert( func2(a, 17)==19 ); 933 static assert( !__traits(compiles, func2()) ); 934 } 935 936 /// Convert a value to bitwise or of enum members 937 string bitEnumToString(T)(ulong val) pure @safe { 938 import std.format : format; 939 940 static assert( is(T==enum), "enumToString must accept an enum type (duh!). Got " ~ T.stringof ~ " instead." ); 941 942 string ret; 943 944 foreach(enumMember; EnumMembers!T) { 945 if( (val & enumMember)!=0 ) { 946 if( ret.length!=0 ) 947 ret ~= "|"; 948 949 ret ~= text(enumMember); 950 val &= ~(enumMember); 951 } 952 } 953 954 if( val!=0 ) { 955 if( ret.length!=0 ) 956 ret ~= "|"; 957 958 ret ~= format("0x%x", val); 959 } 960 961 return ret; 962 } 963 964 unittest { 965 import mecca.lib.exception : assertEQ; 966 967 enum Test : uint { 968 A = 1, 969 B = 4, 970 C = 8, 971 D = 4, 972 } 973 974 assertEQ( bitEnumToString!Test(2), "0x2" ); 975 assertEQ( bitEnumToString!Test(8), "C" ); 976 assertEQ( bitEnumToString!Test(11), "A|C|0x2" ); 977 assertEQ( bitEnumToString!Test(12), "B|C" ); 978 } 979 980 /** 981 Returns $(D true) if $(D R) is a pseudo input range with ref return. 982 983 An input range must 984 define the primitives $(D empty), $(D popFront), and $(D front). The 985 following code should compile for any input range. 986 987 ---- 988 R r; // can define a range object 989 if (r.empty) {} // can test for empty 990 r.popFront(); // can invoke popFront() 991 auto h = r.front; // can get the front of the range of non-void type 992 ---- 993 994 The last line fails if the underlying type is non-copyable. It is still possible 995 to define a pseudo input range that works with non-copyable types by having `r.front` 996 return a reference, but `isInputRange` will return `false` for it. 997 998 Params: 999 R = type to be tested 1000 1001 Returns: 1002 true if R is a pseudo InputRange with ref front, false if not 1003 */ 1004 template isRefInputRange(R) { 1005 static if( 1006 is(typeof(R.init) == R) && 1007 is(ReturnType!((R r) => r.empty) == bool) && 1008 __traits(hasMember, R, "front") && 1009 [__traits(getFunctionAttributes, R.front)].any!("a == \"ref\"") && 1010 is(typeof((R r) => r.popFront)) 1011 ) 1012 { 1013 enum bool isRefInputRange = true; 1014 } else { 1015 enum bool isRefInputRange = false; 1016 } 1017 } 1018 1019 1020 unittest { 1021 struct S { 1022 int a; 1023 1024 void popFront() { 1025 } 1026 1027 @property bool empty() { return true; } 1028 1029 @property ref int front() return { return a; } 1030 } 1031 1032 static assert( isRefInputRange!S ); 1033 } 1034 1035 // Returns a reference to a (maybe private) member of a class or a struct 1036 @("notrace") auto ref accessMember(string NAME, T, bool recurse=true)(auto ref T obj) if (is(T == class) || is(T == struct)) { 1037 static assert (__traits(hasMember, obj, NAME), "accessMember " ~ NAME ~ " on " ~ T.stringof ~ " which has no such member"); 1038 enum aliasThis = __traits(getAliasThis, T); 1039 1040 static if (recurse && aliasThis.length > 0) { 1041 enum aliasThisName = aliasThis[0]; 1042 static if (!is(typeof(accessMember!(NAME, T, false)(obj)) == void)) { 1043 return accessMember!(NAME, T, false)(obj); 1044 } 1045 else { 1046 return accessMember!NAME(accessMember!(aliasThisName, T, false)(obj)); 1047 } 1048 } 1049 else { 1050 foreach(i, _; typeof(T.tupleof)) { 1051 static if (__traits(identifier, T.tupleof[i]) == NAME) { 1052 return obj.tupleof[i]; 1053 } 1054 } 1055 assert (false); 1056 } 1057 }