Two other ways to implement the concept. The first works with optional arguments and the other requires all arguments.
/** * Optional arguments and no default values; might not compile if all args * not passed. */ --------------------------------------------------------------------------- /** * A templated function useful for creating aliases for naming function * parameters. */ auto namedArguments(string name, T)(T arguments) { static struct Args { enum string argName = name; T argValue; alias argValue this; } return Args(cast(Unqual!T)arguments); } /// unittest { alias arg1 = namedArguments!("arg1", string); alias arg2 = namedArguments!("arg2", int); void namedArgsFunc(T...)(T arguments) { const vars = parseArguments!()(arguments); const string one = vars.arg1; const int two = vars.arg2; } namedArgsFunc(arg1(""), arg2(42)); namedArgsFunc(arg2(56), arg1("fred")); } private enum isNamedArgument(T) = hasMember!(T, "argName") && hasMember! (T, "argValue"); /** * Parse parameters of the type returned from $(D namedArguments) into a * named tuple usable in a function. */ auto parseArguments(T...)(T inputs) if (allSatisfy!(isNamedArgument, T)) { template GetTypeAndName(ArgT) { alias Type = typeof(ArgT.argValue); enum string Name = ArgT.argName; alias GetTypeAndName = TypeTuple!(Type, Name); } auto ret = Tuple!(staticMap!(GetTypeAndName, T))(); foreach (arg; inputs) { mixin(`ret.` ~ arg.argName) = arg.argValue; } return ret; } unittest { alias arg1 = namedArguments!("arg1", string); alias arg2 = namedArguments!("arg2", int); const test1 = parseArguments(arg1("string"), arg2(42)); assert(test1.arg1 == "string"); assert(test1.arg2 == 42); const test2 = parseArguments(arg2(42), arg1("string")); assert(test2.arg1 == "string"); assert(test2.arg2 == 42); } -------------------------------------------------------------------------- ///All required arguments -------------------------------------------------------------------------- /** * A templated function useful for creating aliases for naming function * parameters. * * Params: * ArgTypesT = The enum type for all named parameters in a group. * * Returns: A voldemort type that can be handled by $(D parseArguments). */ template namedArguments(ArgTypesT) if (is(ArgTypesT == enum)) { auto namedArguments(ArgTypesT type, T)(T arguments) { static struct Args { alias ArgType = ArgTypesT; enum ArgType argsType = type; T args; alias args this; } return Args(cast(Unqual!T)arguments); } } /// unittest { enum ArgTypes { Arg1, Arg2 } alias FuncArgsT = namedArguments!ArgTypes; alias arg1 = FuncArgsT!(ArgTypes.Arg1, string); alias arg2 = FuncArgsT!(ArgTypes.Arg2, int); void namedArgsFunc(T...)(T arguments) { const vars = parseArguments(arguments); const string one = vars.Arg1; const int two = vars.Arg2; } namedArgsFunc(arg1(""), arg2(42)); namedArgsFunc(arg2(56), arg1("fred")); } private enum isNamedArgument(T) = hasMember!(T, "argsType") && hasMember! (T, "args"); /** * Parse parameters of the type returned from $(D namedArguments) into a * named tuple usable in a function. */ auto parseArguments(T...)(T inputs) if (allSatisfy!(isNamedArgument, T)) { template GetTypeAndName(ArgT) { import std.conv : to; alias Type = typeof(ArgT.args); enum string Name = ArgT.argsType.to!string(); alias GetTypeAndName = TypeTuple!(Type, Name); } auto ret = Tuple!(staticMap!(GetTypeAndName, T))(); foreach (I, arg; inputs) { ret[I] = arg.args; } return ret; } unittest { enum ArgTypes { Arg1, Arg2 } alias FuncArgsT = namedArguments!ArgTypes; alias arg1 = FuncArgsT!(ArgTypes.Arg1, string); alias arg2 = FuncArgsT!(ArgTypes.Arg2, int); const test1 = parseArguments(arg1("string"), arg2(42)); assert(test1.Arg1 == "string"); assert(test1.Arg2 == 42); const test2 = parseArguments(arg2(42), arg1("string")); assert(test2.Arg1 == "string"); assert(test2.Arg2 == 42); } -------------------------------------------------------------------------- Jonathan