Variadic functions: How to pass another variadic function the variadic args?

monarch_dodra monarchdodra at gmail.com
Sun Aug 4 11:36:56 PDT 2013


On Sunday, 4 August 2013 at 15:29:48 UTC, Ali Çehreli wrote:
> On 08/03/2013 09:05 AM, monarch_dodra wrote:
>
> > On Saturday, 3 August 2013 at 15:10:20 UTC, Ali Çehreli wrote:
> >> On 08/03/2013 07:58 AM, bearophile wrote:
> >>
> >> > Gabi:
> >> >
> >> >>   //HOW TO pass F1(..) the args we were called with ?
> >> >
> >> >
> >> > import std.stdio;
> >> >
> >> > void f1(Args...)(Args args) {
> >> >      foreach (arg; args)
> >> >          arg.writeln;
> >>
> >> Would you expect the following two lines behave the same?
> >>
> >>     writeln(args);
> >>     writefln("%s", args);
> >
> > I wouldn't.
> >
> >> Apparently not:
> >>
> >> 10hello1.5
> >> 10
> >>
> >> Why?
> >
> > writeln simply prints all the args it receives, then a line
> break.
> >
> > writefln, on the other end, only prints its single "fmt" arg.
> The rest
> > of the args are only used as they are referenced in fmt. Your
> code
> > basically boils down to:
> >
> > writefln("%s", 10, "hello", 1.5);
> > => 10
>
> Does args have a type that makes it a single entity? If so, the 
> two should behave the same. For example, the output is the same 
> tuples and slices:
>
> import std.stdio;
> import std.typecons;
>
> void foo(Args...)(Args args)
> {
>     // Both outputs are the same:
>     writeln(tuple(1, "abc", 1.5));
>     writefln("%s", tuple(1, "abc", 1.5));
>
>     // Both outputs are the same:
>     writeln([ 1, 2, 3 ]);
>     writefln("%s", [ 1, 2, 3 ]);
>
>     // Unfortunately, not here:
>     writeln(args);
>     writefln("%s", args);
>
>     // Let's see why that may be...
>
>     // Prints "(int, string, double)"
>     writeln(Args.stringof);
> }
>
> void main()
> {
>     foo(10, "hello", 1.5);
> }
>
> According to the last line in foo(), args is three types 
> together. Is that a TypeTuple I wonder... Yes, it is a 
> TypeTuple:
>
> import typetuple;
>
> // ...
>
>     // Passes
>     static assert(
>         is (Args == typeof(TypeTuple!(int.init, string.init, 
> double.init))));
>
> Now I see... Since writefln is also a variadic function, it 
> naturally peels off the TypeTuple one %s at a time.
>
> It is silly of me to stumble on this :) but I learned that 
> there exists one type that is printed differently by the 
> following two lines:
>
>     writeln(a);
>     writefln("%s", a);
>
> Can you think of any other type of 'a' that would be different? 
> (Modulo formatting like square brackets around slices, commas 
> between elements, etc.)
>
> Ali

Yes, you can build a single "std.typecons.Tuple" out of a 
"generic tuple of values". However, this prints because the 
*type* "Tuple" is itself printed with all its internals, as 
opposed to writefln itself extracting the members of the Tuple. 
Any format specifier would be lost, for example.

Note that technically, the type "TypeTuple" doesn't exist. It's a 
template that simply forwards "Args". The type itself does not 
exist. When you write "TypeTuple!Args", it is immediatly replaced 
by "Args" by the compiler. It's only use is that it allows some 
syntax that the compiler would not know how to parse without a 
bit of help.

For example, if your write "pragma(msg, Args.stringof)", it'll 
print:
"(int, string, double)".

As you can see, no TypeTuple. Here, the use of TypeTuple is 
obvious though: The code:
"static assert(is(Args == (int, string, double));"
This would not compile, as the compiler would not understand 
those tokens. TypeTuple is a simple way of adding grouping, 
without confusing the compiler.

Another interesting aspect of TypeTuple (that I found confusing 
at first), is that since it is not a type "per se", a TypeTyple 
of a TypeTuple is simply the expanded TypeTuple:
TypeTuple!(TypeTuple!(int, string), TypeTuple!(int, string));
is
TypeTuple!(int, string, int, string);
is
(int, string, int, string);

An interesting read which came up recently:
"Variadic grouping"
http://forum.dlang.org/thread/wyokmbxiuetwigbpyumz@forum.dlang.org

Long story short, if you need actual grouping then a plain old:

template Group(Args...)
{
     alias Ungroup = Args;
}
or
template Pack(Args...)
{
     alias Unpack = Args;
}

Using this will achieve passing several Args as a single grouped 
type. However, since it is explicitly packed/grouped, extracting 
the actual members will also need to be explicit.


More information about the Digitalmars-d-learn mailing list