As discussed in DConf2015: Python-like keyword arguments
Liran Zvibel via Digitalmars-d
digitalmars-d at puremagic.com
Sun May 31 04:56:47 PDT 2015
On Friday, 29 May 2015 at 17:46:12 UTC, Liran Zvibel wrote:
> I think we should try to create a wrapper function taking the
> original function as (alias F) that leverages
> ParameterIdentifierTuple , ParameterTypeTuple and
> ParameterDefaultValueTuple to generate the namedArguments
> automatically.
Ketmar's work is very impressive (and I think it's better to have
a nice syntax though the compiler), but since I've promised to
work on it on the plane and actually did, I'm sending my
implementation of a library "function" that implements Python
like kw argument calling.
It support positional arguments, keyword arguments and defaults.
Please not that this is just a basic implementation, and will
probably need some more tweaking to really support all types.
Feel free to take this implementation and make sure it works in
the real world :)
Liran
as gist: https://gist.github.com/liranz/d1a42b47f8d744db2c69
as code: below
=================
import std.traits;
import std.typetuple;
import std.typecons;
import std.string;
import std.stdio;
import std.conv;
auto KWArg(string name, T)(T arguments)
{
static struct Args
{
T argValue;
enum argName = name;
alias argType = T;
alias argValue this;
}
return Args(cast(Unqual!T)arguments);
}
private enum isKWArgument(T) = hasMember!(T, "argName") &&
hasMember!(T, "argValue");
template KWFunc(alias fun) {
struct KWFunc {
static int getNumOfPositionalArguments(int
curr,ARGS...)() {
alias types = ParameterTypeTuple!fun;
static if (ARGS.length == 0) {
return curr; // case that all are positional
}
alias first = ARGS[0];
static if (!isKWArgument!first) {
static assert(is(first : types[curr]),
format("positional argument of
wrong type. Expected %s found %s",
types[curr].stringof,
ARGS[0].stringof));
return getNumOfPositionalArguments!(curr +1,
ARGS[1..$])();
} else { // Finished all positional, lets make sure
rest are KWArgs
foreach(k, A; ARGS) {
static assert(isKWArgument!A);
}
return curr;
}
}
static int getPositionByArgName(int positionalArguments,
string name, ARGS...)() {
int ret;
foreach(j, A; ARGS[positionalArguments .. $]) {
static if (name == A.argName) {
return j + positionalArguments;
}
}
return -1;
}
static string generateCallerString(ARGS...)() {
alias names = ParameterIdentifierTuple!fun;
alias types = ParameterTypeTuple!fun;
alias defaults = ParameterDefaultValueTuple!fun;
string ret = "fun(";
enum positionalArguments =
getNumOfPositionalArguments!(0, ARGS)();
foreach (i, n; names) {
static if (i != 0) {
ret ~= ", ";
}
static if (i < positionalArguments) {
ret ~= format("args[%s]", i);
} else {
enum argumentPosition =
getPositionByArgName!(positionalArguments, n, ARGS);
static if (-1 != argumentPosition) {
alias current = ARGS[argumentPosition];
static assert(n == current.argName,
format("KW Argument name
ended up wrong. Expected '%s' found '%s'",
n, current.argName));
static assert(is(current.argType ==
types[i]),
format("KW argument with
name %s and type '%s' conflicts ofiginal type '%s'",
n,
current.argType.stringof, types[i].stringof));
ret ~= format("args[%d]",
argumentPosition);
} else {
static if (!is(defaults[i] : void)) {
ret ~= to!string(defaults[i]);
} else {
// We were not able to place any
argument. Announce failure
static assert(false, format("Could
not place anything for argument #%s : %s %s",
i,
types[i].stringof, n));
}
}
}
}
ret ~= " );";
return ret;
}
static auto opCall(ARGS...)(ARGS args) {
enum ret = generateCallerString!ARGS();
static if(is(ReturnType!func == void)) {
mixin(ret);
} else {
mixin("return " ~ ret);
}
}
}
}
string normalFunc(string pos0, int pos1, string arg1, int arg2,
long arg3 = 50, long arg4 = 100) {
return format("pos0 is '%s', pos1 is '%s', arg1 is '%s' arg2
is '%s' arg3 is '%s' arg4 is '%s'", pos0, pos1, arg1, arg2, arg3,
arg4);
}
unittest
{
alias arg2 = KWArg!("arg2", int);
// test 2 positional argument, out of order KW arguments and
default value
auto ret = KWFunc!normalFunc("positional", 144,
KWArg!"arg1"("full fledged"), KWArg!"arg4"(22L), arg2(2));
assert(ret == "pos0 is 'positional', pos1 is '144', arg1 is
'full fledged' arg2 is '2' arg3 is '50' arg4 is '22'");
//TODO: Add more test cases :)
}
More information about the Digitalmars-d
mailing list