void delegate () Paste (string name, string joiner) { return { static if (more) enum @name = { _@name @joiner Next.name; }; else enum @name = { _@name; }; } } private template MessageStrings(string msgName, T...) { static assert(T.length > 1, "Message parameters must be in pairs"); enum more = T.length > 2; static if (more) { alias MessageStrings!(msgName, T[2..$]) Next; } static if (T.length > 0) { static assert(!hasAliasing!(T[0]), "Cannot use type " ~ T[0].stringof ~ " in a message"); static assert(is(typeof(T[1]) : string), "Message parameters must be named"); enum _fieldStr = { @(T[0].stringof) @T[1]; }; enum _initStr = { this.@T[1] = @T[1]; } enum _readStr = { @t[1] = stream.get!@T[0].stringof; } enum _writeStr = { (@T[1]) }; enum _paramStr = { @(T[0].stringof) @T[1]; }; enum _nameStr = { @T[1]; }; enum _callStr = { message.@msgName.@T[1]; }; @Paste("fieldStr", "\n "); // field types and names in definition @Paste("initStr", "\n "); // field assignments in constructor @Paste("readStr", "\n "); // fields read from an InStream @Paste("writeStr", ""); // fields written to an OutStream @Paste("paramStr", ", "); // field types and names, comma-separated @Paste("nameStr", ", "); // field names, comma separated @Paste("callStr", ", "); // field values accessed in an enclosing union, comma separated } } void delegate () Messages (T...) (string name) { static assert(T.length > 0, "Messages have to contain fields"); alias name msgName; alias MessageStrings!(name, T) strings; return { struct @(name)Msg { @strings.fieldStr; this (@strings.paramStr) { @strings.initStr; } void read (InStream stream) { @strings.readStr; } void write (OutStream stream) { stream@strings.writeStr; } } }; }