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 }