Comment #0 by andrej.mitrovich — 2012-09-18T09:09:36Z
A simplified version of std.range.lockstep:
auto newLockstep(Args...)(Args args, StoppingPolicy stoppingPolicy = StoppingPolicy.shortest)
{
// ...
}
However it doesn't work:
foreach (x, y; newLockstep([1, 2], [1, 2])) { }
test.d(32): Error: template test.newLockstep does not match any
function template declaration
test.d(14): Error: template test.newLockstep(Args...) if
(allSatisfy!(isInputRange,staticMap!(Unqual,Args))) cannot deduce
template function from argument types !()(int[],int[])
Passing an argument in place of the default explicitly does work:
foreach (x, y; newLockstep([1, 2], [1, 2], StoppingPolicy.shortest)) { }
Comment #1 by andrej.mitrovich — 2013-06-22T00:22:18Z
A better use-case is:
void foo(Args...)(Args args, string file = __FILE__, size_t line = __LINE__)
{
}
This would allow us to construct error messages with file + line but without creating template bloat because file and line usually have to become template value arguments in order to support variadic arguments. IOW the above currently has to be:
void foo(string file = __FILE__, size_t line = __LINE__, Args...)(Args args)
{
}
But this creates too much template bloat. I've seen other people ask for this feature, I think schveiguy (Steven Schveighoffer) mentioned it as well.
Here's an example use-case, where an enforce-style function can take a format string and avoid the need to call format() explicitly from the call-site:
/** Similar to $(D enforce), except it can take a formatting string as the second argument. */
T require(T, Args...)
(T value, lazy Args args, string file = __FILE__, size_t line = __LINE__)
{
static if (Args.length)
string msg = Args.length > 1 ? format(args[0], args[1 .. $]) : text(args);
else
enum msg = "requirement failed.";
if (!value)
throw new Exception(msg, file, line);
return value;
}
unittest
{
string file = "foo.d";
require(file.exists, "File '%s' does not exist.", file);
}
Currently this won't compile unless you make file and line compile-time arguments.
@Kenji: Do you think it's possible to implement this without a big disruption (e.g. code breakage)?
Comment #2 by andrej.mitrovich — 2013-06-30T10:19:58Z
Please note that in 2.063 the compiler now ICEs on this unsupported feature:
-----
void foo(T...)(T args, string file = __FILE__) { }
void main()
{
foo();
}
-----
> Assertion failure: 'index < dim' on line 462 in file 'root\root.h'
Comment #3 by bugzilla — 2013-10-06T23:41:37Z
(In reply to comment #2)
> Please note that in 2.063 the compiler now ICEs on this unsupported feature:
>
> -----
> void foo(T...)(T args, string file = __FILE__) { }
>
> void main()
> {
> foo();
> }
> -----
>
> > Assertion failure: 'index < dim' on line 462 in file 'root\root.h'
With 2.064 head, produces:
test.d(5): Error: template test.foo does not match any function template declaration. Candidates are:
test.d(1): test.foo(T...)(T args, string file = __FILE__)
test.d(5): Error: template test.foo(T...)(T args, string file = __FILE__) cannot deduce template function from argument types !()()
Removed ice keyword.
Comment #4 by issues.dlang — 2017-04-18T10:09:25Z
It looks like the status quo right now (with the development version of dmd 2.075) is that default arguments work with variadic templates if the template is explicitly instantiated but not if IFTI is used. So,
-----
void foo(T...)(T args, string file = __FILE__) { }
void main()
{
foo();
}
-----
won't compile, but
-----
void foo(T...)(T args, string file = __FILE__) { }
void main()
{
foo!int(42);
}
-----
will. So, it looks like this is probably now purely an IFTI issue. And presumably, IFTI could be made to just assume that the default arguments are always used (since it has no way of differentiating between explicit arguments for those parameters and variadic arguments that have the same type, and anyone who wants to provid explicit arguments can always explicitly instantiate the template, which even works right now). That _seems_ like it would be straightforward, but I'm not at all familiar with how IFTI is implemented in the compiler.
Regardless, having it fixed so that the default arguments compile when IFTI is used would have a significant impact on stuff like std.experimental.logger, which currently has __FILE__, __LINE__, __FUNCTION__, __PRETTY_FUNCTION__, and __MODULE__ as default template arguments, which results in a function template being instantiated for every log call.
Comment #5 by code — 2018-01-07T00:18:57Z
While working on limited lifetimes (@safe RC/Uniq) I ran into another use-case where a default argument is needed.
// use implicit allocator argument
RC!T rc(T, Alloc, Args...)(Args args, return scope Alloc = Mallocator.instance) if (!Args.length || !isAllocator!(Args[$-1]));
Adding the default argument inside of the function body (instead of in the signature), wouldn't be able to convey the lifetime information from the argument to the return value, hence it has to be a default argument which is evaluated on the call-site.
An algorithm for IFTI would be fairly simple, greedily match the variadic parameter to all remaining arguments, then add in all the default arguments.
The above case would have another overload for non-default allocators.
// last argument is explicit allocator
RC!T rc(T, Args...)(return scope Args args) if (Args.length && isAllocator!(Args[$-1]));
Indeed this shouldn't be too complex to support in the compiler.
Comment #6 by timothee.cour2 — 2018-02-04T05:00:11Z