www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to create TypeTuple/ExpressionTuple from tuple/tuples

reply =?UTF-8?B?IsOYaXZpbmQi?= <oivind.loe gmail.com> writes:
Given e.g. a function template

void f(T ...)(T t) {
   writeln(t.length);
}

How can I call this function with an already-constructed tuple 
but pass the pule as an expressiontuple?

auto v = tuple(1, 2, 3);
f(v);

In the case above, f will print 1 because 1 tuple is given to the 
function, but I want to 'flatten' the tuple before the call, 
making the function see all the values in the tuple and thus 
print 3. I cannot change the function.

Similarly, I want to be able to do this:

auto v0 = tuple(1, 2, 3);
auto v1 = tuple(4, 5, 6);
f(some_function_to_combine_and_flatten_tuples(v0, v1));

And have f print 6.. Calling f(v0, v1) will print 2.

One reason for needing this is that I am using the receive 
function in std.concurrency, and I want to be able to merge 
message handlers from multiple locations.
Aug 07 2012
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/7/12, "=D8ivind" <oivind.loe gmail.com> wrote:
 How can I call this function with an already-constructed tuple
 but pass the pule as an expressiontuple?

 auto v =3D tuple(1, 2, 3);
 f(v);

Use the .expand property: f(v.expand)
Aug 07 2012
prev sibling next sibling parent =?UTF-8?B?IsOYaXZpbmQi?= <oivind.loe gmail.com> writes:
On Tuesday, 7 August 2012 at 16:11:05 UTC, Andrej Mitrovic wrote:
 On 8/7/12, "Øivind" <oivind.loe gmail.com> wrote:
 How can I call this function with an already-constructed tuple
 but pass the pule as an expressiontuple?

 auto v = tuple(1, 2, 3);
 f(v);

Use the .expand property: f(v.expand)

Works like a charm. Thanks! The last one then becomes f(v0.expand, v1.expand)
Aug 07 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/7/12, "=D8ivind" <oivind.loe gmail.com> wrote:
 The last one then becomes f(v0.expand, v1.expand)

Yeah. It's somewhat possible to use a helper function for multiple TypeTuples, but the problem is D functions cannot return language tuples (only TypeTuple from std.typecons which is what the tuple() call creates) so you would always need to call "expand" before the function call, e.g.: void f(T...)(T t) { writeln(t.length); } auto expand(T...)(T t) { static if (T.length =3D=3D 1) { return t[0]; } else static if (T.length > 1) { return tuple(t[0].expand, expand(t[1..$]).expand); } } void main() { auto v0 =3D tuple(1, 2, 3); auto v1 =3D tuple(4, 5, 6); auto v2 =3D tuple(7, 8, 9); f(expand(v0, v1, v2).expand); } Once D is improved enough to be able to return language tuples this should be easier to use.
Aug 07 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Tue, Aug 7, 2012 at 7:35 PM, Andrej Mitrovic
<andrej.mitrovich gmail.com> wrote:


 Yeah. It's somewhat possible to use a helper function for multiple
 TypeTuples, but the problem is D functions cannot return language
 tuples (only TypeTuple from std.typecons which is what the tuple()
 call creates) so you would always need to call "expand" before the
 function call, e.g.:

You can also take the 'dual' of your solution and have a template that makes a function automatically expand tuples: alias expander!foo efoo; // efoo is a function that accepts tuples and automatically cracks them open for you, and then pass them to foo auto result = efoo(tuple(1,2), "abc", tuple('a', 3.14)); assert(result = foo(1,2,"abc", 'a', 3.14)); The same idea could be use to have a function 'expand' any user-defined type, be they structs or classes, by applying .tupleof on them. The code for expander is a little bit involved, I'm afraid, I had to use a helper struct. import std.traits; import std.typecons; struct Fill(Expanded...) { Expanded expanded; void from(int index = 0, TupleOrNot...)(TupleOrNot args) { static if (TupleOrNot.length > 0) { static if (isTuple!(TupleOrNot[0])) // It's a tuple, expand it ... { expanded[index..index+TupleOrNot[0].length] = args[0].expand; from!(index+TupleOrNot[0].length)(args[1..$]); // ... and continue the recursion } else // it's not a tuple, let it that way... { expanded[index] = args[0]; from!(index+1)(args[1..$]); // ...and continue the recursion } } } } /** Take a function fun and return a transformed function that will expand std.typecons.Tuple's provided as arguments and let other args untouched */ template expander(alias fun) if (isSomeFunction!fun) { ReturnType!fun expander(Args...)(Args args) { Fill!(ParameterTypeTuple!fun) f; // prepare the future arguments f.from(args); // does the expansion job, the arguments are stored in f.expanded return fun(f.expanded); } } void main() { int foo(int i, double d, string s, char c) { return i + cast(int)d + s.length + (c - '0');} alias expander!foo efoo; writeln( foo(1, 3.14, "abc", 'a') ); writeln( efoo(tuple(1, 3.14, "abc", 'a')) ); writeln( efoo(tuple(1, 3.14), "abc", 'a') ); writeln( efoo(tuple(1, 3.14), tuple("abc", 'a')) ); }
Aug 07 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/7/12, Philippe Sigaud <philippe.sigaud gmail.com> wrote:
 You can also take the 'dual' of your solution and have a template that
 makes a function automatically expand tuples

But that doesn't work for variadic templates, which is the OP's case: int foo(T...)(T t) { return 1; } alias expander!foo efoo; // error
Aug 07 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Tue, Aug 7, 2012 at 11:31 PM, Andrej Mitrovic
<andrej.mitrovich gmail.com> wrote:
 On 8/7/12, Philippe Sigaud <philippe.sigaud gmail.com> wrote:
 You can also take the 'dual' of your solution and have a template that
 makes a function automatically expand tuples

But that doesn't work for variadic templates, which is the OP's case: int foo(T...)(T t) { return 1; } alias expander!foo efoo; // error

The template can easily be modified to deal with template functions, but in that case, you lose the parameter-type verification, for example if the function template has a constraint.
Aug 07 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Wed, Aug 8, 2012 at 7:01 AM, Philippe Sigaud
<philippe.sigaud gmail.com> wrote:

 But that doesn't work for variadic templates, which is the OP's case:

 int foo(T...)(T t) { return 1; }
 alias expander!foo efoo;  // error

The template can easily be modified to deal with template functions, but in that case, you lose the parameter-type verification, for example if the function template has a constraint.

Like this: /// Helper template to flatten a Tuple template Expand(T) { static if (isTuple!T) alias T.Types Expand; else alias T Expand; } /** Take a function fun and return a transformed function that will expand std.typecons.Tuple's provided as arguments and let other args untouched */ template expander(alias fun) { auto expander(Args...)(Args args) { static if (isSomeFunction!fun) alias ParameterTypeTuple!fun Expanded; else alias staticMap!(Expand, Args) Expanded; // flatten the tuples Expanded expanded; /// Does the filling work: put values extracted from t in expanded void from(int index = 0, TupleOrNot...)(TupleOrNot t) { static if (TupleOrNot.length > 0) { static if (isTuple!(TupleOrNot[0])) // It's a tuple, expand it ... { expanded[index..index+TupleOrNot[0].length] = t[0].expand; from!(index+TupleOrNot[0].length)(t[1..$]); } else // it's not a tuple, let it that way... { expanded[index] = t[0]; from!(index+1)(t[1..$]); // ...and continue the recursion } } } from(args); return fun(expanded); } } int foo(Ts...)(Ts ts) { return Ts.length; } void main() { alias expander!foo efoo; writeln( foo(1, 3.14, "abc", 'a') ); // 4 writeln( efoo(tuple(1, 3.14, "abc", 'a')) ); // 4 writeln( efoo(tuple(1, 3.14), "abc", 'a') ); // 4 writeln( efoo(tuple(1, 3.14), tuple("abc", 'a')) ); // 4 }
Aug 07 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/8/12, Philippe Sigaud <philippe.sigaud gmail.com> wrote:
 Like this..

That's really cool. Could you put this in the DT book, along with your previous sample? Good job btw!
Aug 08 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Wed, Aug 8, 2012 at 7:19 PM, Andrej Mitrovic
<andrej.mitrovich gmail.com> wrote:
 On 8/8/12, Philippe Sigaud <philippe.sigaud gmail.com> wrote:
 Like this..

That's really cool. Could you put this in the DT book, along with your previous sample? Good job btw!

Thanks. Hey, every time I do something like that, you ask me to put it in my template tutorial ;) I'm not sure it's *so* useful. Maybe a generic destructuring template, that opens structs, classes, arrays?
Aug 08 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/8/12, Philippe Sigaud <philippe.sigaud gmail.com> wrote:
 Hey, every time I do something like that, you ask me to put it in my
 template tutorial ;)

Well yeah, we don't want a future Banana(tm) company patenting our codez, we need prior art! :p So maybe it's not book-worthy. Perhaps there should be a github repo with mildly interesting boost-licensed code that anyone can use. Or maybe I'm overthinking it.
Aug 08 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Wed, Aug 8, 2012 at 7:45 PM, Andrej Mitrovic
<andrej.mitrovich gmail.com> wrote:
 On 8/8/12, Philippe Sigaud <philippe.sigaud gmail.com> wrote:
 Hey, every time I do something like that, you ask me to put it in my
 template tutorial ;)

Well yeah, we don't want a future Banana(tm) company patenting our codez, we need prior art! :p So maybe it's not book-worthy. Perhaps there should be a github repo with mildly interesting boost-licensed code that anyone can use. Or maybe I'm overthinking it.

Like the Python Cookbook? There was a dsource project like this IIRC, but it was for small modules, not code snippets.
Aug 08 2012
prev sibling next sibling parent "Kenji Hara" <k.hara.pg gmail.com> writes:
On Tuesday, 7 August 2012 at 16:11:05 UTC, Andrej Mitrovic wrote:
 On 8/7/12, "Øivind" <oivind.loe gmail.com> wrote:
 How can I call this function with an already-constructed tuple
 but pass the pule as an expressiontuple?

 auto v = tuple(1, 2, 3);
 f(v);

Use the .expand property: f(v.expand)

You can also use slice operator instead of expand property. import std.stdio, std.typecons; void f(T ...)(T t) { writeln(t.length); } void main(){ auto v = tuple(1, 2, 3); f(v[]); // prints 3 auto v0 = tuple(1, 2, 3); auto v1 = tuple(4, 5, 6); f(v0[], v1[]); // prints 6 }
Aug 08 2012
prev sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Thu, Aug 9, 2012 at 1:26 AM, Kenji Hara <k.hara.pg gmail.com> wrote:

 You can also use slice operator instead of expand property.

 import std.stdio, std.typecons;

 void f(T ...)(T t) {
     writeln(t.length);
 }

 void main(){

     auto v = tuple(1, 2, 3);
     f(v[]); // prints 3

I didn't know that. Nice one. Indeed, it's almost like a range. A bit dangerous, as people might forget that what they get is a tuple with possibly different types, instead of a range, but good to know as it offers a homogeneous syntax. Hey, I have map/filter/reduce functions for tuples, should they be part of Phobos? auto t = tupleMap!(a => to!string(a))(1,'a',"abc'", 3.14); // eager, not lazy assert(t == tuple("1", "a", "abc", "3.14")); auto arr = [t[]]; // crack open & transform into an array assert(is(typeof(arr) == string[])); Of course, the mapping function must be polymorphic or overloaded. The good news is, it may very well have different return types for each argument type, thus giving back another, entirely different tuple. But I'm not sure people use tuples that much in D...
     auto v0 = tuple(1, 2, 3);
     auto v1 = tuple(4, 5, 6);
     f(v0[], v1[]);  // prints 6
 }

Aug 08 2012