1 /** 2 Module contains UniversalDelegate, usefull to change complicated delegates to void delegate(void) 3 */ 4 module mutils.job_manager.universal_delegate; 5 6 import std.meta : AliasSeq; 7 import std.traits : isDelegate, isFunctionPointer, Parameters, ParameterStorageClass, ParameterStorageClassTuple, ReturnType; 8 9 auto makeUniversalDelegate(T, Args...)(T del, Args args) { 10 alias pstc = ParameterStorageClassTuple!T; 11 foreach (arg; pstc) { 12 static assert(arg != ParameterStorageClass.ref_, "Delegates with ref parameters can not passed without specifying exact delegate type.\n Change from makeUniversalDelegate(&del) to makeUniversalDelegate!(typeof(&del)(&del)."); 13 } 14 return UniversalDelegate!(T)(del, args); 15 } 16 17 auto makeUniversalDelegate(T)(T del, Parameters!(T) args) { 18 return UniversalDelegate!(T)(del, args); 19 } 20 /** 21 Struct which stores all parameters for given delegate. 22 Mainly used to store delegate with patameters for future call. 23 May be used to convert: AnyType deletage(SomeTypes..) to void delegate() 24 25 Ref parameters are stored as a pointers 26 */ 27 struct UniversalDelegate(Delegate) { 28 static assert(is(Delegate == function) || isFunctionPointer!Delegate 29 || isDelegate!Delegate, "Provided type has to be: delegate, function, function pointer"); 30 enum hasReturn = !is(ReturnType!Delegate == void); 31 Delegate deleg; 32 getDelegateArgumentsSave!Delegate argumentsSave; //for ref variables pointer is saved 33 static if (hasReturn){ 34 ReturnType!Delegate result; 35 } 36 37 this(Delegate del, Parameters!Delegate args) { 38 static assert(Parameters!(Delegate).length == args.length, "Parameters have to match"); 39 alias pstc = ParameterStorageClassTuple!Delegate; 40 deleg = del; 41 foreach (i, ref a; args) { 42 static if (pstc[i] == ParameterStorageClass.ref_) { 43 argumentsSave[i] = &a; 44 } else { 45 argumentsSave[i] = a; 46 } 47 } 48 } 49 50 void* getFuncPtr() { 51 static if (is(Delegate == delegate)) { 52 return deleg.funcptr; 53 54 } else { 55 return deleg; 56 57 } 58 } 59 60 ReturnType!Delegate call() { 61 // Load arguments to orginal form 62 Parameters!Delegate argumentsTmp; 63 alias pstc = ParameterStorageClassTuple!Delegate; 64 foreach (i, a; argumentsSave) { 65 static if (pstc[i] == ParameterStorageClass.ref_) { 66 argumentsTmp[i] = *a; 67 } else { 68 argumentsTmp[i] = a; 69 } 70 } 71 // Call 72 static if (hasReturn) { 73 ReturnType!Delegate result = deleg(argumentsTmp); 74 } else { 75 deleg(argumentsTmp); 76 } 77 // Assign ref values to theirs orginal location 78 foreach (i, a; argumentsSave) { 79 static if (pstc[i] == ParameterStorageClass.ref_) { 80 *a = argumentsTmp[i]; 81 } 82 } 83 static if (hasReturn){ 84 return result; 85 } 86 } 87 88 void callAndSaveReturn() { 89 static if (hasReturn) { 90 result = call(); 91 } else { 92 call(); 93 } 94 } 95 96 97 ubyte[] toBytes(){ 98 ubyte* me=cast(ubyte*)&this; 99 return me[0..typeof(this).sizeof]; 100 } 101 102 static void callFromBytes(ubyte* ptr){ 103 auto me=cast(typeof(this)*)ptr; 104 me.callAndSaveReturn(); 105 } 106 } 107 108 template getPointer(T) { 109 alias getPointer = T*; 110 } 111 112 ///Replaces ref variables with pointer 113 private template getDelegateArgumentsSave(Delegate) { 114 alias getDelegateArgumentsSave = getDelegateArgumentsSaveImpl!( 115 ParameterStorageClassTuple!Delegate, Parameters!Delegate).result; 116 } 117 118 private template getDelegateArgumentsSaveImpl(args...) if (args.length % 2 == 0) { 119 enum half = args.length / 2; 120 alias pstc = args[0 .. half]; 121 alias tuple = args[half .. $]; 122 123 static if (tuple.length) { 124 alias head = tuple[0]; 125 alias tail = tuple[1 .. $]; 126 alias next = getDelegateArgumentsSaveImpl!(AliasSeq!(pstc[1 .. $], tuple[1 .. $])).result; 127 static if (pstc[0] == ParameterStorageClass.ref_) 128 alias result = AliasSeq!(getPointer!head, next); 129 else 130 alias result = AliasSeq!(head, next); 131 } else { 132 alias result = AliasSeq!(); 133 } 134 } 135 136 @nogc nothrow: 137 /// Using Deleagte 138 unittest { 139 static struct TestTmp { 140 @nogc nothrow int add(int a, int b, ref ulong result) { 141 result = a + b; 142 return a + b; 143 } 144 } 145 146 TestTmp test; 147 ulong returnByRef; 148 auto universalDelegate = makeUniversalDelegate!(typeof(&test.add))(&test.add, 149 2, 2, returnByRef); 150 auto result = universalDelegate.call(); 151 assert(result == 4); 152 assert(returnByRef == 4); 153 154 } 155 156 /// Using Function 157 unittest { 158 @nogc nothrow int add(int a, int b, ref ulong result) { 159 result = a + b; 160 return a + b; 161 } 162 163 ulong returnByRef; 164 auto universalDelegate = makeUniversalDelegate!(typeof(&add))(&add, 2, 2, returnByRef); 165 auto result = universalDelegate.call(); 166 assert(result == 4); 167 assert(returnByRef == 4); 168 } 169 // void with no parameters 170 unittest { 171 static int someNum; 172 static @nogc nothrow void add() { 173 someNum = 200; 174 } 175 176 auto universalDelegate = makeUniversalDelegate!(typeof(&add))(&add); 177 universalDelegate.call(); 178 assert(someNum == 200); 179 }