1 module orderedrpc;
2 
3 import treeserial;
4 
5 import std.traits;
6 import std.meta;
7 
8 import std.algorithm;
9 import std.range;
10 
11 enum RPCSrc {
12 	self	= 0x1,
13 	remote	= 0x2,
14 }
15 
16 struct RPC {
17 	ubyte id;
18 }
19 struct RPCCon(T) {
20 	alias Connection = T;
21 }
22 
23 class RPCError : Exception {
24 	this(string msg, string file = __FILE__, size_t line = __LINE__) {
25 		super(msg, file, line);
26 	}
27 }
28 
29 template rpcsWithID(alias symbol) {
30 	template Match(short id_, alias mem_) {
31 		enum id = id_;
32 		alias mem = mem_;
33 	}
34 	template f(alias last, alias mem) {
35 		alias udas = getUDAs!(mem, RPC);
36 		static assert(udas.length == 1);
37 		static if (is(udas[$-1])) {
38 			alias f = Match!(last.id+1, mem);
39 		}
40 		else {
41 			alias f = Match!(udas[$-1].id, mem);
42 		}
43 	}
44 	alias rpcsWithID = staticScan!(f, Match!(-1, null), getSymbolsByUDA!(symbol, RPC));
45 }
46 
47 enum rpcIDs(alias symbol)() {
48 	ubyte next = 0;
49 	ubyte[] ids = [];
50 	foreach (mem; getSymbolsByUDA!(symbol, RPC)) {
51 		alias udas = getUDAs!(mem, RPC);
52 		static assert(udas.length == 1);
53 		static if (is(udas[$-1])) {
54 			assert(next+1 != 0);
55 			ids ~= next++;
56 		}
57 		else {
58 			ids ~= udas[$-1].id;
59 			next = udas[$-1].id+1;
60 		}
61 	}
62 	return ids;
63 }
64 template rpcByID(alias symbol, ubyte id) {
65 	import std.algorithm;
66 	alias rpcByID = getSymbolsByUDA!(symbol, RPC)[rpcIDs!symbol.countUntil(id)];
67 }
68 
69 template RPCMsgData(alias f) {
70 	////static if (Parameters!(rpc!src).length == 1) {
71 	////	static assert(__traits(compiles, rpc!src(data[1..$].deserialize!(Parameters!(rpc!src)[0]))), "RPC function must take either 1 serializable argument or 2 arguments, a `Connection` and another serializable one (either order).");
72 	////	rpc!src(data[1..$].deserialize!(Parameters!(rpc!src)[0]));
73 	////}
74 	////else static if (Parameters!(rpc!src).length == 2) {
75 	////	static if (__traits(compiles, rpc!src(connection, data[1..$].deserialize!(Parameters!(rpc!src)[1]))))
76 	////		rpc!src(connection, data[1..$].deserialize!(Parameters!(rpc!src)[1]));
77 	////	else static if (__traits(compiles, rpc!src(data[1..$].deserialize!(Parameters!(rpc!src)[0]),connection)))
78 	////		rpc!src(data[1..$].deserialize!(Parameters!(rpc!src)[0]), connection);
79 	////	else static assert(false, "RPC function must take either 1 serializable argument or 2 arguments, a `Connection` and another serializable one (either order).");
80 	////}
81 }
82 
83 
84 template rpcFunction(alias f, ConnectionType) {
85 	alias Params = Parameters!f;
86 	static if (is(Params[0] == ConnectionType)) {
87 		pragma(inline, true)
88 		void rpcFunction(Params[1..$] args, ConnectionType connection) {
89 			f(connection, args);
90 		}
91 	}
92 	else static if (is(Params[$-1] == ConnectionType)) {
93 		pragma(inline, true)
94 		void rpcFunction(Params[0..$-1] args, ConnectionType connection) {
95 			f(args, connection);
96 		}
97 	}
98 	else {
99 		pragma(inline, true)
100 		void rpcFunction(Params args, ConnectionType connection) {
101 			f(args);
102 		}
103 	}
104 }
105 template rpcFunction(alias f) {
106 	alias Params = Parameters!f;
107 	pragma(inline, true)
108 	void rpcFunction(Params args) {
109 		f(args);
110 	}
111 }
112 template RPCParameters(alias f, ConnectionType) {
113 	alias Params = Parameters!f;
114 	static if (is(Params[0] == ConnectionType)) {
115 		alias RPCParameters = Params[1..$];
116 	}
117 	else static if (is(Params[$-1] == ConnectionType)) {
118 		alias RPCParameters = Params[0..$-1];
119 	}
120 	else {
121 		alias RPCParameters = Params;
122 	}
123 }
124 template RPCParameters(alias f) {
125 	alias RPCParameters = Parameters!f;
126 }
127 
128 template RPCParametersExt(alias rpc, alias RPCSrc, RPCSrc src) {
129 	alias RPCParametersExt = RPCParameters!(rpc!(src), ConType!(src));
130 }
131 template rpcParametersMatch(alias rpc, alias RPCSrc, RPCSrc srcs) {
132 	static foreach (i; 0..flags(srcs).length-1) {
133 		alias Matcher = aliasSeqMatch!(RPCParametersExt!(rpc,RPCSrc,flags(srcs)[i]));
134 		static if (!is(defined) && !Matcher!(RPCParametersExt!(rpc,RPCSrc,flags(srcs)[i+1]))) {
135 			enum rpcParametersMatch = false;
136 			enum defined;
137 		}
138 	}
139 	static if (!is(defined))
140 		enum rpcParametersMatch = true;
141 }
142 
143 template RPCSendConnections(RPCTrgt, RPCTrgt trgts) {
144 	template ConnectionsImpl(size_t i) {
145 		static if (i==trgts.flags.length)
146 			alias ConnectionsImpl = AliasSeq!();
147 		else {
148 			static if (!(is(ConType!(trgts.flags[i]) == typeof(null)) || is(typeof(ConType!(trgts.flags[i])) == typeof(null))))
149 				alias ConnectionsImpl = AliasSeq!(ConType!(trgts.flags[i])[], ConnectionsImpl!(i+1));
150 			else
151 				alias ConnectionsImpl = AliasSeq!(ConnectionsImpl!(i+1));
152 		}
153 	}
154 	alias RPCSendConnections = ConnectionsImpl!0;
155 }
156 
157 template ConType(alias src) {
158 	static if (hasUDA!(src,RPCCon)) {
159 		alias ConType = getUDAs!(src,RPCCon)[$-1].Connection;
160 	}
161 	else
162 		alias ConType = typeof(null);
163 }
164 
165 template MakeRPCsImpl(alias RPCSrc, alias RPCTrgt) {
166 	import std.meta;
167 	import std.traits;
168 	import std.algorithm;
169 	
170 	static if (is(RPCTrgt)) {
171 		static foreach (i, rpc; getSymbolsByUDA!(typeof(this), RPC)) {
172 			mixin(q{
173 				template }~__traits(identifier, rpc)~q{_send(RPCTrgt trgts) if (is(typeof(rpc!(cast(RPCSrc) trgts.flags[0]))) && rpcParametersMatch!(rpc, RPCSrc, cast(RPCSrc) trgts)) }~"{"~q{
174 					void }~__traits(identifier, rpc)~q{_send(RPCSendConnections!(RPCTrgt,trgts) connections, RPCParametersExt!(rpc, RPCSrc, cast(RPCSrc) (trgts.flags[0])) args) {
175 						ubyte[] data = [rpcIDs!(typeof(this))[i]];
176 						scope(success)
177 							rpcSend!trgts(data);
178 						foreach (i, arg; args) {
179 							static if (__traits(compiles, arg.serialize)) {
180 								static if (i==args.length-1)
181 									alias Attributes = NoLength;
182 								else
183 									alias Attributes = AliasSeq!();
184 								data ~= arg.serialize!Attributes;
185 							}
186 							else throw new RPCError("Parameter \""~typeof(arg).stringof~"\" of RPC \""~__traits(identifier, rpc)~"\" cannot be serialized.");
187 						}
188 					}
189 				}~"}"~q{
190 			});
191 		}
192 	}
193 	void rpcRecv(RPCSrc src)(const(ubyte)[] data) {
194 		enum ids = rpcIDs!(typeof(this));
195 		ubyte msgID = data.deserialize!ubyte;
196 		static foreach(id; ids) {
197 			if (msgID == id) {
198 				alias rpcTemplate = rpcByID!(typeof(this),id);
199 				alias rpc = rpcFunction!((Parameters!(rpcTemplate!src) args)=>rpcTemplate!src(args));
200 				alias Params = Parameters!rpc;
201 				Params args;
202 				scope (success)
203 					rpc(args);
204 				foreach (i, Param; Params) {
205 					static if (__traits(compiles, (lvalueOf!(const(ubyte)[])).deserialize!Param)) {
206 						static if (i==Params.length-1)
207 							alias Attributes = NoLength;
208 						else
209 							alias Attributes = AliasSeq!();
210 						args[i] = data.deserialize!(Param, Attributes);
211 					}
212 					else throw new RPCError("Parameter \""~Param.stringof~"\" of RPC \""~__traits(identifier, rpcTemplate)~"\" cannot be deserialized.  If it is a ConnectionType then you called the wrong rpcRecv (call rpcRecv with a connection).");
213 				}
214 			}
215 		}
216 	}
217 	
218 	void rpcRecv(RPCSrc src)(ConType!src connection, const(ubyte)[] data){//// if (!is(ConType!(RPCSrc, src) == typeof(null))) {
219 		enum ids = rpcIDs!(typeof(this));
220 		ubyte msgID = data.deserialize!ubyte;
221 		static foreach(id; ids) {
222 			if (msgID == id) {
223 				alias rpcTemplate = rpcByID!(typeof(this),id);
224 				alias rpc = rpcFunction!((Parameters!(rpcTemplate!src) args)=>rpcTemplate!src(args), ConType!src);
225 				alias Params = Parameters!rpc[0..$-1];
226 				Params args;
227 				scope (success)
228 					rpc(args, connection);
229 				foreach (i, Param; Params) {
230 					static if (__traits(compiles, (lvalueOf!(const(ubyte)[])).deserialize!Param)) {
231 						static if (i==Params.length-1)
232 							alias Attributes = NoLength;
233 						else
234 							alias Attributes = AliasSeq!();
235 						args[i] = data.deserialize!(Param, Attributes);
236 					}
237 					else throw new RPCError("Parameter `"~Param.stringof~"` of RPC `"~__traits(identifier, rpcTemplate)~"` cannot be deserialized.");////  If you think this should be the connection type, then for `"~RPCSrc.stringof~"."~__traits(identifier, src)~"` it should be of type `"~ConType!src.stringof~"` (Correct parameter or `SrcType` or src).");
238 				}
239 			}
240 		}
241 	}
242 }
243 
244 template MakeRPCsArgs(This, Ts...) {
245 	static foreach (T; Ts) {
246 		static if (is(T==enum)) {
247 			static if (!is(MakeRPCs_RPCSrc)) {
248 				alias MakeRPCs_RPCSrc = T;
249 			}
250 			else static if (!is(MakeRPCs_RPCTrgt)) {
251 				alias MakeRPCs_RPCTrgt = T;
252 				static assert(is(typeof(This.rpcSend)), "`MakeRPCs` was given a second enum for `RPCTrgt`, but `rpcSend` is undefined.");
253 				////static assert(is(typeof(rpcSend!(rvalueOf!RPCTrgt))), "`rpcSend` is undefined, but not right, rpcSend must be: `void rpcSend(RPCTrgt trgts)(...)`");
254 			}
255 			else static assert(false, "`MakeRPCs` was given more than 2 enums.  Accepts 0-2.");
256 		}
257 		else static assert(false);
258 	}
259 	static if (!is(MakeRPCs_RPCSrc)) {
260 		alias MakeRPCs_RPCSrc = RPCSrc;
261 	}
262 	static if (!is(MakeRPCs_RPCTrgt)) {
263 		static if (is(typeof(This.rpcSend)))
264 			alias MakeRPCs_RPCTrgt = MakeRPCs_RPCSrc;
265 		else
266 			enum MakeRPCs_RPCTrgt = null; 
267 		////static assert(is(typeof(rpcSend!(rvalueOf!RPCTrgt))), "`rpcSend` is undefined, but not right, rpcSend must be: `void rpcSend(RPCTrgt trgts)(...)`.  Either give an appropriate RPCTrgt (pass a second enum to `MakeRPCs`) or `rpcSend` should work with `RPCSrc`.");
268 	}
269 	
270 	//---This section is necessary because of some bug, without this `ConType` will not see the UDAs attached the the enum members.
271 	template ExtractConTypes(E, size_t i) {
272 		static if (i < EnumMembers!E.length)
273 			alias ExtractConTypes = AliasSeq!(ConType!(EnumMembers!E[i]), ExtractConTypes!(E, i+1));
274 		else
275 			alias ExtractConTypes = AliasSeq!();
276 	}
277 	
278 	alias RPCSrcConTypes = ExtractConTypes!(MakeRPCs_RPCSrc, 0);
279 	static if (!is(typeof(MakeRPCs_RPCTrgt) == typeof(null))) {
280 		alias RPCTrgtConTypes = ExtractConTypes!(MakeRPCs_RPCTrgt, 0);
281 	}
282 	else {
283 		enum RPCTrgtConTypes = null;
284 	}
285 	//---
286 	
287 	alias MakeRPCsArgs = AliasSeq!(MakeRPCs_RPCSrc, MakeRPCs_RPCTrgt/***, MakeRPCs_SrcType, MakeRPCs_TrgtType*//***, RPCSrcConTypes, RPCTrgtConTypes*/);
288 }
289 
290 mixin template MakeRPCs(Ts...) {
291 	mixin MakeRPCsImpl!(MakeRPCsArgs!(typeof(this),Ts));
292 }
293 
294 // Fixer for D bug #20835
295 enum enumMemberUDAFixMixin(string enumName) = q{
296 	static foreach(i; 0..EnumMembers!}~enumName~q{.length)
297 		pragma(msg, __traits(getAttributes, EnumMembers!}~enumName~q{[i]));
298 };
299 
300 unittest {
301 	pragma(msg, "Compiling Test A");
302 	import std.stdio;
303 	writeln("Running Test A");
304 	import std.exception;
305 	import std.conv;
306 	
307 	string lastMsg = "";
308 	class A {
309 		@RPC
310 		void msg(RPCSrc src=RPCSrc.self)(int x) {
311 			lastMsg = "msg: "~src.to!string~" - "~x.to!string;
312 		}
313 		@RPC
314 		void msg2(RPCSrc src=RPCSrc.self)(float x) {
315 			lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
316 		}
317 		mixin MakeRPCs;
318 	}
319 	A a = new A;
320 	a.msg!(RPCSrc.self)(5);
321 	assert(lastMsg == "msg: self - 5");
322 	a.msg!(RPCSrc.remote)(5);
323 	assert(lastMsg == "msg: remote - 5");
324 	a.msg(5);
325 	assert(lastMsg == "msg: self - 5");
326 	a.rpcRecv!(RPCSrc.remote)(0 ~ serialize(1));
327 	assert(lastMsg == "msg: remote - 1");
328 	a.rpcRecv!(RPCSrc.remote)(1 ~ serialize(1.5f));
329 	assert(lastMsg == "msg2: remote - 1.5");
330 }
331 unittest {
332 	pragma(msg, "Compiling Test B");
333 	import std.stdio;
334 	writeln("Running Test B");
335 	import std.exception;
336 	import std.conv;
337 	
338 	string lastMsg = "";
339 	class Connection {}
340 	enum RPCSrc {
341 		self	= 0x1,
342 		@RPCCon!Connection remote	= 0x2,
343 	}
344 	mixin(enumMemberUDAFixMixin!"RPCSrc");// Necessary because of D bug #20835
345 	class A {
346 		@RPC
347 		void msg(RPCSrc src=RPCSrc.self)(int x) {
348 			lastMsg = "msg: "~src.to!string~" - "~x.to!string;
349 		}
350 		@RPC
351 		template msg2(RPCSrc src=RPCSrc.self) {
352 			static if (src == RPCSrc.self)
353 			void msg2 (float x) {
354 				lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
355 			}
356 			static if (src == RPCSrc.remote)
357 			void msg2 (float x, Connection con) {
358 				lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
359 			}
360 		}
361 		mixin MakeRPCs!(RPCSrc);
362 	}
363 	A a = new A;
364 	a.msg!(RPCSrc.self)(5);
365 	assert(lastMsg == "msg: self - 5");
366 	a.msg!(RPCSrc.remote)(5);
367 	assert(lastMsg == "msg: remote - 5");
368 	a.msg(5);
369 	assert(lastMsg == "msg: self - 5");
370 	a.rpcRecv!(RPCSrc.remote)(0 ~ serialize(1));
371 	assert(lastMsg == "msg: remote - 1");
372 	assertThrown!Throwable(a.rpcRecv!(RPCSrc.remote)(1 ~ serialize(1.5f)));
373 	a.rpcRecv!(RPCSrc.remote)(new Connection(), 0 ~ serialize(1));
374 	assert(lastMsg == "msg: remote - 1");
375 	a.rpcRecv!(RPCSrc.remote)(new Connection(), 1 ~ serialize(1.5f));
376 	assert(lastMsg == "msg2: remote - 1.5");
377 }
378 unittest {
379 	pragma(msg, "Compiling Test C");
380 	import std.stdio;
381 	writeln("Running Test C");
382 	import std.exception;
383 	import std.conv;
384 	
385 	string lastMsg = "";
386 	ubyte[] lastSendData = [];
387 	class A {
388 		void rpcSend(RPCSrc src)(ubyte[] data) {
389 			lastSendData = data;
390 		}
391 		@RPC
392 		void msg(RPCSrc src=RPCSrc.self)(int x) {
393 			lastMsg = "msg: "~src.to!string~" - "~x.to!string;
394 		}
395 		mixin MakeRPCs;
396 	}
397 	A a = new A;
398 	a.msg_send!(RPCSrc.remote)(5);
399 	assert(lastSendData == [0,5,0,0,0]);
400 }
401 unittest {
402 	pragma(msg, "Compiling Test D");
403 	import std.stdio;
404 	writeln("Running Test D");
405 	import std.exception;
406 	import std.conv;
407 	
408 	string lastMsg = "";
409 	ubyte[] lastSendData = [];
410 	class A {
411 		void rpcSend(RPCSrc src)(ubyte[] data) {
412 			lastSendData = data;
413 		}
414 		@RPC
415 		void msg(RPCSrc src=RPCSrc.self)(int x) {
416 			lastMsg = "msg: "~src.to!string~" - "~x.to!string;
417 		}
418 		@RPC
419 		void msg2(RPCSrc src=RPCSrc.self)(float x) {
420 			lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
421 		}
422 		mixin MakeRPCs;
423 	}
424 	A a = new A;
425 	a.msg_send!(RPCSrc.remote)(5);
426 	assert(lastSendData == [0,5,0,0,0]);
427 	a.msg2_send!(RPCSrc.remote)(1.5);
428 	assert(lastSendData == (cast(ubyte)1) ~ 1.5f.serialize);
429 }
430 unittest {
431 	pragma(msg, "Compiling Test E");
432 	import std.stdio;
433 	writeln("Running Test E");
434 	import std.exception;
435 	import std.conv;
436 	
437 	string lastMsg = "";
438 	ubyte[] lastSendData = [];
439 	class Connection {}
440 	enum RPCSrc {
441 		self	= 0x1,
442 		@RPCCon!Connection remote	= 0x2,
443 	}
444 	mixin(enumMemberUDAFixMixin!"RPCSrc");// Necessary because of D bug #20835
445 	class A {
446 		void rpcSend(RPCSrc trgts)(ubyte[] data) {
447 			lastSendData = data;
448 		}
449 		@RPC
450 		template msg(RPCSrc src=RPCSrc.self) {
451 			static if (src == RPCSrc.self)
452 			void msg (int x) {
453 				lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
454 			}
455 			static if (src == RPCSrc.remote)
456 			void msg (int x, Connection con) {
457 				lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
458 			}
459 		}
460 		@RPC
461 		template msg2(RPCSrc src=RPCSrc.self) {
462 			static if (src == RPCSrc.self)
463 			void msg2 (float x) {
464 				lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
465 			}
466 			static if (src == RPCSrc.remote)
467 			void msg2 (float x, long y) {
468 				lastMsg = "msg2: "~src.to!string~" - "~x.to!string~" - "~y.to!string;
469 			}
470 		}
471 		mixin MakeRPCs!(RPCSrc);
472 	}
473 	pragma(msg, ConType!(RPCSrc.remote));
474 	A a = new A;
475 	a.msg_send!(RPCSrc.self)(5);
476 	assert(lastSendData == [0,5,0,0,0]);
477 	a.msg_send!(RPCSrc.remote)([new Connection], 5);
478 	assert(lastSendData == [0,5,0,0,0]);
479 	a.msg_send!(RPCSrc.self|RPCSrc.remote)([new Connection], 5);
480 	assert(lastSendData == [0,5,0,0,0]);
481 	a.msg2_send!(RPCSrc.self)(1.5);
482 	assert(lastSendData == (cast(ubyte)1) ~ 1.5f.serialize);
483 	a.msg2_send!(RPCSrc.remote)([new Connection], 1.5, 2);
484 	assert(lastSendData == (cast(ubyte)1) ~ 1.5f.serialize ~ 2L.serialize);
485 	assert(!__traits(compiles, a.msg2_send!(RPCSrc.self | RPCSrc.remote)(1.5)));
486 	assert(!__traits(compiles, a.msg2_send!(RPCSrc.self | RPCSrc.remote)(1.5, 2)));
487 }
488 unittest {
489 	pragma(msg, "Compiling Test Fa");
490 	import std.stdio;
491 	writeln("Running Test Fa");
492 	import std.exception;
493 	import std.conv;
494 	
495 	enum Src {
496 		server = 0x1,
497 		client = 0x2,
498 	}
499 	enum Trgt {
500 		client = 0x1,
501 		server = 0x2,
502 	}
503 	
504 	ubyte[] lastSendData = [];
505 	string lastMsg = "";
506 	class A {
507 		void rpcSend(Trgt trgts)(ubyte[] data) {
508 			lastSendData = data;
509 		}
510 		@RPC
511 		void msg(Src src)(int x) {
512 			lastMsg = "msg: "~src.to!string~" - "~x.to!string;
513 		}
514 		mixin MakeRPCs!(Src, Trgt);
515 	}
516 	A a = new A;
517 	a.msg_send!(Trgt.server)(5);
518 	assert(lastSendData == [0, 5,0,0,0]);
519 }
520 unittest {
521 	pragma(msg, "Compiling Test Fb");
522 	import std.stdio;
523 	writeln("Running Test Fb");
524 	import std.exception;
525 	import std.conv;
526 	
527 	enum Src {
528 		server = 0x1,
529 		client = 0x2,
530 	}
531 	enum Trgt {
532 		client = 0x1,
533 		server = 0x2,
534 	}
535 	
536 	ubyte[] lastSendData = [];
537 	string lastMsg = "";
538 	class A {
539 		void rpcSend(Trgt trgts)(ubyte[] data) {
540 			lastSendData = data;
541 		}
542 		@RPC
543 		void msg(Src src:Src.client)(int x) {
544 			lastMsg = "msg: "~src.to!string~" - "~x.to!string;
545 		}
546 		@RPC
547 		void msg2(Src src:Src.server)(long x) {
548 			lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
549 		}
550 		mixin MakeRPCs!(Src, Trgt);
551 	}
552 	A a = new A;
553 	a.msg_send!(Trgt.server)(5);
554 	assert(lastSendData == [0, 5,0,0,0]);
555 	a.msg2_send!(Trgt.client)(5);
556 	assert(lastSendData == [1, 5,0,0,0, 0,0,0,0]);
557 	assert(!__traits(compiles, a.msg_send!(Trgt.client)(5)));
558 	assert(!__traits(compiles, a.msg2_send!(Trgt.server)(5)));
559 }
560 unittest {
561 	pragma(msg, "Compiling Test G");
562 	import std.stdio;
563 	writeln("Running Test G");
564 	import std.exception;
565 	import std.conv;
566 	
567 	string lastMsg = "";
568 	ubyte[] lastSendData = [];
569 	class RemoteConnection {}
570 	class SelfConnection {}
571 	enum RPCSrc {
572 		@RPCCon!SelfConnection self	= 0x1,
573 		@RPCCon!RemoteConnection remote	= 0x2,
574 	}
575 	mixin(enumMemberUDAFixMixin!"RPCSrc");// Necessary because of D bug #20835
576 	class A {
577 		void rpcSend(RPCSrc trgts)(ubyte[] data) {
578 			lastSendData = data;
579 		}
580 		@RPC
581 		template msg(RPCSrc src=RPCSrc.self) {
582 			static if (src == RPCSrc.self)
583 			void msg (int x) {
584 				lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
585 			}
586 			static if (src == RPCSrc.remote)
587 			void msg (int x, RemoteConnection con) {
588 				lastMsg = "msg2: "~src.to!string~" - "~x.to!string;
589 			}
590 		}
591 		mixin MakeRPCs!(RPCSrc);
592 	}
593 	A a = new A;
594 	a.msg_send!(RPCSrc.self)([new SelfConnection], 5);
595 	assert(lastSendData == [0,5,0,0,0]);
596 	a.msg_send!(RPCSrc.remote)([new RemoteConnection], 5);
597 	assert(lastSendData == [0,5,0,0,0]);
598 	a.msg_send!(RPCSrc.self|RPCSrc.remote)([new SelfConnection], [new RemoteConnection], 5);
599 	assert(lastSendData == [0,5,0,0,0]);
600 }
601 
602 template staticScan(alias f, List...)
603 if (List.length > 1)
604 {
605 	static if (List.length == 2)
606 	{
607 		alias staticScan = f!(List[0], List[1]);
608 	}
609 	else
610 	{
611 		alias staticScan = AliasSeq!(f!(List[0], List[1]), staticScan!(f, f!(List[0], List[1]), List[2..$]));
612 	}
613 }
614 template staticFold(alias f, List...)
615 if (List.length > 1)
616 {
617 	static if (List.length == 2)
618 	{
619 		alias staticFold = f!(List[0], List[1]);
620 	}
621 	else
622 	{
623 		alias staticFold = staticFold!(f, f!(List[0], List[1]), List[2..$]);
624 	}
625 }
626 template staticLift(alias f) {
627 	template staticLift(ts...) {
628 		enum staticLift = f(ts);
629 	}
630 }
631 template aliasSeqMatch(As...) {
632 	template aliasSeqMatch(Bs...) {
633 		enum aliasSeqMatch = is(As==Bs);
634 	}
635 }
636 template parametersMatch(alias f, alias b) {
637 	enum parametersMatch = is(Parameters!f == Parameters!b);
638 }
639 unittest {
640 	void a(int, float);
641 	void b(int, float);
642 	void c(int, long);
643 	assert(parametersMatch!(a,b));
644 	assert(!parametersMatch!(a,c));
645 	assert(parametersMatch!(b,a));
646 	assert(!parametersMatch!(c,b));
647 }
648 template allParametersMatch(Ts...) {
649 	static foreach (i; 0..Ts.length-1) {
650 		static if (!is(defined) && !parametersMatch!(Ts[i],Ts[i+1])) {
651 			enum allParametersMatch = false;
652 			enum defined;
653 		}
654 	}
655 	static if (!is(defined))
656 		enum allParametersMatch = true;
657 }
658 unittest {
659 	void a(int, float);
660 	void b(int, float);
661 	void c(int, float);
662 	void d(int, long);
663 	assert(allParametersMatch!(a,b,c));
664 	assert(!allParametersMatch!(a,b,d));
665 	assert(allParametersMatch!(c,b,a));
666 	assert(!allParametersMatch!(a,d,c));
667 }
668 ////template flags(alias fs) if (is(typeof(fs)==enum)) {
669 ////	enum flags = Filter!(staticLift!(f=>f & fs), EnumMembers!(typeof(fs)));
670 ////}
671 ////template flags(F, F fs) if (is(F==enum)) {
672 ////	enum flags = Filter!(staticLift!(f=>f & fs), EnumMembers!(typeof(fs)));
673 ////}
674 F[] flags(F)(F fs) {
675 	return filter!(f=>f & fs)([EnumMembers!(F)]).array;
676 }
677 unittest {
678 	enum E {
679 		a = 0x1,
680 		b = 0x2,
681 		c = 0x4,
682 	}
683 	assert(flags(E.a|E.c) == [E.a,E.c]);
684 }
685 
686 template Exclam(alias t, Ts...) {
687 	alias Exclam = t!Ts;
688 }
689 template AliasSeqOf(alias R) if (isInputRange!(typeof(R))) {
690     import std.typetuple : TT = TypeTuple;
691     static if (R.empty)
692         alias AliasSeqOf = TT!();
693         else
694         alias AliasSeqOf = TT!(R.front(), AliasSeqOf!(R.dropOne()));
695 }
696 
697