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