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;
			}
		}
	};
}