As discussed in DConf2015: Python-like keyword arguments

Jonathan Crapuchettes via Digitalmars-d digitalmars-d at puremagic.com
Fri May 29 08:16:56 PDT 2015


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


More information about the Digitalmars-d mailing list