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 }