Skip to content

Instantly share code, notes, and snippets.

@liranz
Created May 31, 2015 11:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save liranz/d1a42b47f8d744db2c69 to your computer and use it in GitHub Desktop.
Save liranz/d1a42b47f8d744db2c69 to your computer and use it in GitHub Desktop.
KW arguments implemented as a library "function"
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 :)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment