0 AMessage
1 BMessage
2 CMessage
auto stream = serialize!(ByteBackend)(SomeObject, "name"); auto object = deserialize!(ByteBackend, SomeClass)(stream, "name");
interface Message { void opCall(); }
class AMsg : Message { int a; string b; this() {} this(int pa, string pb) { a = pa; b = pb; } void opCall() { writeln("AMsg call with ", a, " ", b); } }
mixin template ProtocolPool(IndexType, SerializerBackend, pairs...) { }
mixin ProtocolPool!(int, BinaryBackend, 0, AMsg, 1, BMsg, 2, CMsg );
template CheckPairs(tpairs...) { static if(tpairs.length > 1) { static assert(__traits(compiles, typeof(tpairs) ), "ProtocolPool expected index first, but got some type"); static assert(is(typeof(tpairs[0]) == IndexType), "ProtocolPool expected index first of type "~ IndexType.stringof~ " not a "~typeof(tpairs[0]).stringof); static assert(is(tpairs[1] : Message), "ProtocolPool expected class implementing Message"~ " interface following index not a "~tpairs[1].stringof); static assert(CountValInList!(tpairs[0], pairs) == 1, "ProtocolPool indexes must be unique! One message,"~ "one index."); enum CheckPairs = CheckPairs!(tpairs[2..$]); } else { static assert(tpairs.length == 0, "ProtocolPool expected even number of parameters. Index and message type."); enum CheckPairs = 0; } }
mixin template ProtocolPool(IndexType, SerializerBackend, pairs...) { template CheckPairs(tpairs...) { // , } static assert(CheckPairs!pairs == 0, "Parameters check failed! If code works well, you never will see this message!"); }
// returns count of val occurenes in list template CountValInList(IndexType val, list...) { static if(list.length > 1) { static if(list[0] == val) enum CountValInList = 1 + CountValInList!(val, list[2..$]); else enum CountValInList = CountValInList!(val, list[2..$]); } else enum CountValInList = 0; }
// generating switch template GenerateSwitch() { template GenerateSwitchBody(tpairs...) { static if(tpairs.length > 0) { enum GenerateSwitchBody = "case("~to!string(tpairs[0])~ "): return cast(Message)(func!(SerializerBackend, "~ tpairs[1].stringof~")(args)); break; \n" ~ GenerateSwitchBody!(tpairs[2..$]); } else enum GenerateSwitchBody = ""; } enum GenerateSwitch = "switch(id)\n{\n"~ GenerateSwitchBody!(pairs) ~ "default: " ~ " break;\n}"; }
switch(id) { case(0): return cast(Message)(func!(SerializerBackend, AMsg)(args)); break; case(1): return cast(Message)(func!(SerializerBackend, BMsg)(args)); break; case(2): return cast(Message)(func!(SerializerBackend, CMsg)(args)); break; default: break; }
// , nested class , private class dummyClass {} // func - , args Message dispatchMessage(alias func, T...)(IndexType id, T args) { static assert(__traits(compiles, func!(SerializerBackend, dummyClass)(args)), "ChooseMessage func must be callable with got args " ~T.stringof); // , //pragma(msg, GenerateSwitch!()); mixin(GenerateSwitch!()); throw new Exception( "Cannot find corresponding message for id "~to!string(id)~"!"); }
void readMsg(Stream stream) { int id; stream.read(id); writeln("Got message id is ",id); auto message = dispatchMessage!(deserialize)(id, stream, "MSG"); writeln("Calling message"); message(); }
auto stream = constructMessage!AMsg(10, "Hello World!");
template FindMessageId(Msg, tpairs...) { static if(tpairs.length > 0) { static if(is(tpairs[1] == Msg)) enum FindMessageId = tpairs[0]; else enum FindMessageId = FindMessageId!(Msg, tpairs[2..$]); } else static assert(false, "Cannot find id for message "~ Msg.stringof~". Check protocol list."); }
Stream constructMessage(Msg, T...)(T args) { static assert(is(Msg : Message), Msg.stringof~ " must implement Message interface!"); static assert(__traits(compiles, new Msg(args)), Msg.stringof~ " should implement constructor with formal parameters "~ T.stringof); auto msg = new Msg(args); IndexType sendId = FindMessageId!(Msg, pairs); auto stream = serialize!SerializerBackend(msg, "MSG"); auto fullStream = new MemoryStream; fullStream.write(sendId); fullStream.copyFrom(stream); fullStream.position = 0; return fullStream; }
version(unittest) { class AMsg : Message { int a; string b; this() {} this(int pa, string pb) { a = pa; b = pb; } void opCall() { writeln("AMsg call with ", a, " ", b); } } class BMsg : Message { double a; double b; this() {} this(double pa, double pb) { a = pa; b = pb; } void opCall() { writeln("BMsg call with ", a, " ", b); } } class CMsg : Message { double a; string s; this() {} this(double pa, string ps) { a = pa; s = ps; } void opCall() { writeln("CMsg call ", a, " ", s); } } mixin ProtocolPool!(int, GendocArchive, 0, AMsg, 1, BMsg, 2, CMsg ); } unittest { void readMsg(Stream stream) { int id; stream.read(id); writeln("Got message id is ",id); auto message = dispatchMessage!(deserialize)(id, stream, "MSG"); writeln("Calling message"); message(); } // serializing auto stream = constructMessage!BMsg(4.0,8.0); // sending... // got at other side readMsg(stream); stream = constructMessage!AMsg(10, "Hello World!"); readMsg(stream); stream = constructMessage!CMsg(5., "Some usefull string"); readMsg(stream); }
// Copyright Gushcha Anton 2012. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) module protocol; import std.stdio; import std.conv; import std.stream; // , import util.serialization.serializer; interface Message { void opCall(); } mixin template ProtocolPool(IndexType, SerializerBackend, pairs...) { // returns count of val occurenes in list template CountValInList(IndexType val, list...) { static if(list.length > 1) { static if(list[0] == val) enum CountValInList = 1 + CountValInList!(val, list[2..$]); else enum CountValInList = CountValInList!(val, list[2..$]); } else enum CountValInList = 0; } // check pairs to be correct template CheckPairs(tpairs...) { static if(tpairs.length > 1) { static assert(__traits(compiles, typeof(tpairs) ), "ProtocolPool expected index first, but got some type"); static assert(is(typeof(tpairs[0]) == IndexType), "ProtocolPool expected index first of type "~IndexType.stringof~" not a "~typeof(tpairs[0]).stringof); static assert(is(tpairs[1] : Message), "ProtocolPool expected class implementing Message interface following index not a "~tpairs[1].stringof); static assert(CountValInList!(tpairs[0], pairs) == 1, "ProtocolPool indexes must be unique! One message, one index."); enum CheckPairs = CheckPairs!(tpairs[2..$]); } else { static assert(tpairs.length == 0, "ProtocolPool expected even number of parameters. Index and message type."); enum CheckPairs = 0; } } // generating switch template GenerateSwitch() { template GenerateSwitchBody(tpairs...) { static if(tpairs.length > 0) { enum GenerateSwitchBody = "case("~to!string(tpairs[0])~"): return cast(Message)(func!(SerializerBackend, "~tpairs[1].stringof~")(args)); break; \n" ~ GenerateSwitchBody!(tpairs[2..$]); } else enum GenerateSwitchBody = ""; } enum GenerateSwitch = "switch(id)\n{\n"~GenerateSwitchBody!(pairs) ~ `default: ` ~ " break;\n}"; } template FindMessageId(Msg, tpairs...) { static if(tpairs.length > 0) { static if(is(tpairs[1] == Msg)) enum FindMessageId = tpairs[0]; else enum FindMessageId = FindMessageId!(Msg, tpairs[2..$]); } else static assert(false, "Cannot find id for message "~Msg.stringof~". Check protocol list."); } // actual check static assert(CheckPairs!pairs == 0, "Parameters check failed! If code works well, you never will see this message!"); private class dummyClass {} Message dispatchMessage(alias func, T...)(IndexType id, T args) { static assert(__traits(compiles, func!(SerializerBackend, dummyClass)(args)), "ChooseMessage func must be callable with got args "~T.stringof); //pragma(msg, GenerateSwitch!()); mixin(GenerateSwitch!()); throw new Exception("Cannot find corresponding message for id "~to!string(id)~"!"); } Stream constructMessage(Msg, T...)(T args) { static assert(is(Msg : Message), Msg.stringof~" must implement Message interface!"); static assert(__traits(compiles, new Msg(args)), Msg.stringof~" should implement constructor with formal parameters "~T.stringof); auto msg = new Msg(args); IndexType sendId = FindMessageId!(Msg, pairs); auto stream = serialize!SerializerBackend(msg, "MSG"); auto fullStream = new MemoryStream; fullStream.write(sendId); fullStream.copyFrom(stream); fullStream.position = 0; return fullStream; } } version(unittest) { class AMsg : Message { int a; string b; this() {} this(int pa, string pb) { a = pa; b = pb; } void opCall() { writeln("AMsg call with ", a, " ", b); } } class BMsg : Message { double a; double b; this() {} this(double pa, double pb) { a = pa; b = pb; } void opCall() { writeln("BMsg call with ", a, " ", b); } } class CMsg : Message { double a; string s; this() {} this(double pa, string ps) { a = pa; s = ps; } void opCall() { writeln("CMsg call ", a, " ", s); } } mixin ProtocolPool!(int, BinaryBackend, 0, AMsg, 1, BMsg, 2, CMsg ); } unittest { void readMsg(Stream stream) { int id; stream.read(id); writeln("Got message id is ",id); auto message = dispatchMessage!(deserialize)(id, stream, "MSG"); writeln("Calling message"); message(); } // serializing auto stream = constructMessage!BMsg(4.0,8.0); // sending... // Got at other side readMsg(stream); stream = constructMessage!AMsg(10, "Hello World!"); readMsg(stream); stream = constructMessage!CMsg(5., "Some usefull string"); readMsg(stream); }
Source: https://habr.com/ru/post/149809/
All Articles