On 13.11.2013 20:05, Ellery Newcomer wrote:
On Wednesday, 13 November 2013 at 10:51:48 UTC, Simen Kjærås wrote:
On 12.11.2013 18:53, Ellery Newcomer wrote:
It's perfectly possible in D to make this work:
hey, cool impl
*comprehends code*
I mean Ewww
I *can* make that work. I'm not going to.
--
Simen
I concur with the second part.
I decided to abandon sanity. Luckily, only for the named parameters. I
now have this code working:
void test(int a, int b = 2, string c = "foo", int d = 14) {
}
alias test2 = nameify!test;
void main() {
test2(1, Args.d = 4, Args.c = "bar");
}
With reasonable error messages for duplicate and missing parameters, as
well as type mismatches.
Source is attached. I hope God forgives me.
--
Simen
import std.stdio : writeln;
import std.traits : ParameterTypeTuple, ParameterIdentifierTuple,
ParameterDefaultValueTuple;
import std.typetuple : TypeTuple, staticIndexOf, staticMap;
import std.conv : to;
struct NamedArg(string _name, Arg) {
enum name = _name;
Arg value;
alias value this;
}
template isNotNamedArg(T...) if (T.length == 1) {
enum isNotNamedArg = !isNamedArg!T;
}
template isNamedArg(T...) if (T.length == 1) {
static if (is(typeof(T[0]))) {
enum isNamedArg = isNamedArg!(typeof(T[0]));
} else {
enum isNamedArg = is(T[0] == NamedArg!U, U...);
}
} unittest {
assert( isNamedArg!(NamedArg!("a", int)));
assert(!isNamedArg!int);
NamedArg!("a", float) f;
assert(isNamedArg!f);
}
final abstract class Args {
@property
static auto opDispatch(string name, Arg)(Arg arg) {
return NamedArg!(name, Arg)(arg);
}
}
template staticFilter(alias F, T...) {
static if (T.length == 0) {
alias staticFilter = TypeTuple!();
} else static if (T.length == 1) {
static if (F!T) {
alias staticFilter = TypeTuple!(T);
} else {
alias staticFilter = TypeTuple!();
}
} else {
alias staticFilter = TypeTuple!(
staticFilter!(F, T[0..$/2]),
staticFilter!(F, T[$/2..$]),
);
}
}
template Head(T...) if (T.length) {
alias Head = TypeTuple!(T[0]);
}
template Tail(T...) {
static if (T.length) {
alias Tail = T[1..$];
} else {
alias Tail = TypeTuple!();
}
}
template Chop(size_t block, alias F) {
alias Chop = TypeTuple!();
}
template Chop(size_t blocks, alias F, T...) if (T.length && T.length % blocks
== 0) {
alias Chop = TypeTuple!(
F!(T[0..$/blocks]),
Chop!(blocks-1, F, T[$/blocks..$])
);
}
template staticZip(size_t N, alias F) {
alias staticZip = TypeTuple!();
}
template staticZip(size_t N, alias F, T...) {
static if (T.length == 0) {
alias staticZip = TypeTuple!();
} else {
alias staticZip = TypeTuple!(
F!(Chop!(N, Head, T)),
staticZip!(N, F, Chop!(N, Tail, T))
);
}
}
final abstract class ArgInfo(string _name, _type, _defaultValue...) if
(_defaultValue.length == 1) {
enum name = _name;
alias type = _type;
alias value = _defaultValue;
}
template staticToString(T...) {
enum staticToString = T.stringof;
}
template staticNameOf(T) {
enum staticNameOf = T.name;
}
string sortNamedArgs(string[] realNames, string[] defaults, string[] lookHere,
int line = __LINE__, string file = __FILE__) {
string result = "alias sorted = TypeTuple!(";
foreach (i, name; realNames) {
bool found = false;
foreach (j, needle; lookHere) {
if (needle == name) {
if (found) {
return "static assert(false, \"" ~ file ~ "(" ~
to!string(line) ~ "): Duplicate parameter '" ~ needle ~ "'.\");";
}
found = true;
result ~= "lookup!(" ~ to!string(j) ~ ", namedArgs), ";
}
}
if (!found) {
result ~= "lookup!(" ~ to!string(i) ~ ",
paramDefaults[unnamedArgs.length..$]), ";
}
}
return result ~ ");";
}
template lookup(int i, T...) {
alias lookup = T[i];
}
struct Compare(T1, T2, string _name) {
static if (isNamedArg!T1) {
alias Type1 = typeof(T1.value);
} else {
alias Type1 = T1;
}
enum name = _name;
alias Type2 = T2;
}
template checkTypesImpl(int line = __LINE__, string file = __FILE__, int idx,
T...) {
static if(T.length == 0) {
enum checkTypesImpl = true;
} else {
alias T1 = TypeTuple!(T[0].Type1);
alias T2 = TypeTuple!(T[0].Type2);
enum N1 = T[0].name;
static assert(!is(T1 == TypeTuple!void), file ~ "(" ~ to!string(line) ~
"): Missing value for parameter " ~ N1 ~ ".");
static assert(is(T2 : T1), file ~ "(" ~ to!string(line) ~ "): type
mismatch. Expected " ~ T1.stringof ~ ", got " ~ T2.stringof ~ " for parameter "
~ N1 ~ ".");
enum checkTypesImpl = checkTypesImpl!(line, file, idx+1, T[1..$]);
}
}
template checkTypes(int line = __LINE__, string file = __FILE__, int idxStart,
T...) {
enum checkTypes = checkTypesImpl!(line, file, idxStart, staticZip!(3,
Compare, T));
}
template nameify(alias fn) {
auto nameify(int line = __LINE__, string file = __FILE__, T...)(T args) {
auto namedArgs = staticFilter!(isNamedArg, args);
auto unnamedArgs = staticFilter!(isNotNamedArg, args);
static assert(is(typeof(TypeTuple!(unnamedArgs, namedArgs)) == T),
"Named arguments must follow unnamed arguments");
alias paramNames = ParameterIdentifierTuple!fn;
alias paramDefaults = ParameterDefaultValueTuple!fn;
alias paramTypes = ParameterTypeTuple!fn;
alias allArgs = staticZip!(3, ArgInfo,
paramNames[unnamedArgs.length..$],
paramTypes[unnamedArgs.length..$],
paramDefaults[unnamedArgs.length..$]);
mixin(sortNamedArgs(
[paramNames[unnamedArgs.length..$]],
[staticMap!(staticToString,
paramDefaults[unnamedArgs.length..$])],
[staticMap!(staticNameOf, typeof(namedArgs))],
line, file));
static assert(sorted.length ==
paramTypes[unnamedArgs.length..$].length);
static assert(checkTypes!(line, file, unnamedArgs.length,
typeof(sorted), paramTypes[unnamedArgs.length..$],
paramNames[unnamedArgs.length..$]));
fn(unnamedArgs, sorted);
}
}
void test(int a, int b, string c = "foo", int d = 14) {
writeln(a);
writeln(b);
writeln(c);
writeln(d);
}
alias test2 = nameify!test;
void main() {
test2(1, Args.d = 4, Args.c = "bar", Args.b = 13);
}