www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Passing Arguments on in Variadic Functions

reply jmh530 <john.michael.hall gmail.com> writes:
In R, it is easy to have some optional inputs labeled as ... and 
then pass all those optional inputs in to another function. I was 
trying to get something similar to work in a templated D 
function, but I couldn't quite get the same behavior. What I have 
below is what I was able to get working.

This approach gives the correct result, but dmd won't deduce the 
type of the template. So for instance, the second to the last 
line of the unit test requires explicitly stating the types. I 
may as well use the alternate version that doesn't use the 
variadic function (which is simple for this trivial example, but 
maybe not more generally).

Do I have any options to get it more similar to the way R does 
things?

import std.algorithm : sum;
import core.vararg;

auto test(R)(R r)
{
	return sum(r);
}

auto test(R, E)(R r, ...)
{
	if (_arguments.length == 0)
		return test(r);
	else
	{
		auto seed = va_arg!(E)(_argptr);
		return sum(r, seed);
	}
}

auto test_alt(R, E)(R r, E seed)
{
	return sum(r, seed);
}

unittest
{
	int[] x = [10, 5, 15, 20, 30];
	assert(test(x) == 80);
	assert(test!(int[], float)(x, 0f) == 80f);
	assert(test_alt(x, 0f) == 80f);
}
Sep 14 2015
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 14 September 2015 at 19:59:18 UTC, jmh530 wrote:
 In R, it is easy to have some optional inputs labeled as ... 
 and then pass all those optional inputs in to another function. 
 I was trying to get something similar to work in a templated D 
 function, but I couldn't quite get the same behavior. What I 
 have below is what I was able to get working.
You want to generally avoid the varargs and instead use variadic templates. The syntax is similar but a bit different: R test(R, E, Args...)(Args args) { static if(Args.length == 0) // no additional arguments else return sum(args); } or whatever you actually need. But what this does is take any number of arguments of various types but makes that length and type available at compile time for static if inspection. The args represents the whole list and you can loop over it, convert to an array with `[args]` (if they are all compatible types, or pass to another function as a group like I did here. This is the way writeln is implemented btw.
Sep 14 2015
prev sibling parent reply anonymous <anonymous example.com> writes:
On Monday 14 September 2015 21:59, jmh530 wrote:

 This approach gives the correct result, but dmd won't deduce the 
 type of the template. So for instance, the second to the last 
 line of the unit test requires explicitly stating the types. I 
 may as well use the alternate version that doesn't use the 
 variadic function (which is simple for this trivial example, but 
 maybe not more generally).
You can use a variadic template instead: ---- import std.algorithm : sum; auto test(R, E ...)(R r, E e) { return sum(r, e); } unittest { int[] x = [10, 5, 15, 20, 30]; assert(test(x) == 80); assert(test(x, 0f) == 80f); assert(test(x, 0f) == 80f); } ----
Sep 14 2015
parent reply jmh530 <john.michael.hall gmail.com> writes:
Thanks to you both. This works perfect.
Sep 14 2015
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 14 September 2015 at 20:26:56 UTC, jmh530 wrote:
 Thanks to you both. This works perfect.
I noticed that there's some interesting interplay with this technique and default arguments. From below, it is required that you put the ones with default arguments last. If there are only two arguments, then it takes the second to be the default argument. However, I can't actually put just one argument, even though V is variadic and sample is a default. That seems strange to me because it makes it seem like sample really isn't optional. Not sure if this is a bug or on purpose. The only way I can resolve it is by writing another function that just takes x (alternately one that takes x and sample only also works). Also, it doesn't seem to let me put template constraints on V so that I could do something like constrain V to be numeric. import std.algorithm : sum; import std.stdio : writeln; auto test(T, V ...)(T x, V seed, bool sample=true) { auto sum_x = sum(x, seed); if (sample) return sum_x; else return sum_x / 2; } void main() { int[] x = [10, 5, 15, 20, 30]; writeln(test(x, true)); writeln(test(x, false)); float seed = 1; writeln(test(x, seed, true)); writeln(test(x, seed, false)); }
Sep 17 2015
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 17 September 2015 at 17:35:18 UTC, jmh530 wrote:
 I noticed that there's some interesting interplay with this 
 technique and default arguments.
Yeah, it expects the V... to consume the rest of the arguments so it doesn't really leave any room for the default arg. I would actually just make it required or do separate functions with it. Optional and variadics don't mix well together. (You could also loop through and look for a bool argument yourself but that is a bit messier.)
 Also, it doesn't seem to let me put template constraints on V 
 so that I could do something like constrain V to be numeric.
import std.meta; import std.traits; if(allSatisfy!(isNumeric, V)) should do it
Sep 17 2015
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 17 September 2015 at 18:40:56 UTC, Adam D. Ruppe 
wrote:
 import std.meta;
 import std.traits;

 if(allSatisfy!(isNumeric, V))

 should do it
Was not aware of allSatisfy. Thanks.
Sep 17 2015
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 17 September 2015 at 18:40:56 UTC, Adam D. Ruppe 
wrote:
 I would actually just make it required or do separate functions 
 with it. Optional and variadics don't mix well together.

 (You could also loop through and look for a bool argument 
 yourself but that is a bit messier.)
It's a result of dealing with the sum function. The seed parameter will override the type. So for instance one option is to do something like auto test(T)(T x, bool sample=true, T seed=T.init) ignoring the complexity of T.init being a nan for some types, it would also overrule some of the behavior of sum. For instance, if you do sum(x) where x is a dynamic array of floats, then the result is a double instead of a float. I think I could figure out how to look through the arguments for a bool, but wouldn't that make me give up the default value for the bool? Maybe it would require, doing one version of the function defined like auto test(T)(T x, bool sample=true) and then another like auto test(T, U ...)(T x, U y) I could then try to use static ifs to ensure some of the other requirements (like a restriction on length so that it's not called if there is only one argument in y). I probably don't even need to loop, I can just static if that the first one in y is a bool and the second is numeric.
Sep 17 2015
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 17 September 2015 at 21:27:31 UTC, jmh530 wrote:

There's probably a way to clean this up better, but this is what 
I was talking about.

import std.algorithm : sum;
import std.stdio : writeln;
import std.traits : isNumeric;

auto test(T)(T x, bool sample=true)
{
	auto sum_x = sum(x);
	if (sample)
		return sum_x;
	else
		return sum_x / 2;
}

auto test(T, V ...)(T x, V y)
{
	static if (y.length)
	{
		static if (y.length == 2 &&
			   is(typeof(y[0]) == bool) &&
			   isNumeric!(typeof(y[1])))
		{
			auto sum_x = sum(x, y[1]);
			if (y[0])
				return sum_x;
			else
				return sum_x / 2;
		}
	}
}

void main()
{
	int[] x = [10, 5, 15, 20, 30];
	writeln(test(x, true));
	writeln(test(x, false));
	float seed = 1;
	writeln(test(x, true, seed));
	writeln(test(x, false, seed));
}
Sep 17 2015
prev sibling parent reply anonymous <anonymous example.com> writes:
On Thursday 17 September 2015 23:27, jmh530 wrote:

 I think I could figure out how to look through the arguments for 
 a bool, but wouldn't that make me give up the default value for 
 the bool?
If you don't find a bool, you use the default value.
Sep 17 2015
parent jmh530 <john.michael.hall gmail.com> writes:
On Friday, 18 September 2015 at 00:55:12 UTC, anonymous wrote:
 On Thursday 17 September 2015 23:27, jmh530 wrote:

 I think I could figure out how to look through the arguments 
 for a bool, but wouldn't that make me give up the default 
 value for the bool?
If you don't find a bool, you use the default value.
Ah. True.
Sep 18 2015