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