www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Applying a tuple to a function (and more)

reply Juanjo Alvarez <juanjux gmail.com> writes:
Hi,

I've just arrived to D 2.0 and after reading Andrei's book I'm loving
everything I'm seeing (except the bugs, of course).

I wanted to ask how these would be done, because I can't find how to do it:

1. Having the strings, defined at compile time, "MyClass" and "mymethod",
how could I could I get to a delegate to MyClass.mymethod?

2. Is there any way to apply a tuple to a function, expanding as arguments?

Like:

void f(int a, double b) {}
auto tup = tuple(42, 3.14);
f(<magic goes here with tup expanding as 42, 3.14>);

Thanks,
Juanjo
Sep 18 2010
next sibling parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sat, Sep 18, 2010 at 19:59, Juanjo Alvarez <juanjux gmail.com> wrote:

 I wanted to ask how these would be done, because I can't find how to do it:

 1. Having the strings, defined at compile time, "MyClass" and "mymethod",
 how could I could I get to a delegate to MyClass.mymethod?
You can insert them in a piece of code as a string, and then mix it in: enum string code = "auto deleg = &" ~ className ~ "." ~ methodName ~ ";"; // auto deleg = &MyClass.mymethod; mixin(code); // created deleg, you're good to go.
 2. Is there any way to apply a tuple to a function, expanding as arguments?

 Like:

 void f(int a, double b) {}
 auto tup = tuple(42, 3.14);
 f(<magic goes here with tup expanding as 42, 3.14>);
Use the .field or .expand alias, exposed by Tuple!() to get a direct access to the internal expression tuple. f(tup.expand); // cracks tup open Recently, bug 2800 was corrected and you can also directly access a tuple's fields by indexing: double d = tup[1]; // or simply: auto d = tup[1]; Before that, you had to do double d = tup.field[1]; Recently, tuples also got a .length member. That's handy. As for expanding tuples inside functions if you do that regularly, I suggest you use a function adaptor, like this: template tuplify(alias fun) if (isCallable!fun) { ReturnType!fun tuplify(Tuple!(ParameterTypeTuple!fun) tup) { return fun(tup.expand); } } Usage: alias tuplify!f tf; tf(tup); tf is a function, like any other. It accepts a Tuple!(int,double), cracks it open and pass it to f. I don't know if you need some explanation on how it works? Here it is anyway: tuplify is a template, a parameterized piece of code. tuplify takes only one template parameter, by alias. This means you give it a name, any identifier in the current scope. You do not give it a type. That's what allows you to write tuplify!f. Here the result of the template instantation is a function with the same name 'tuplify'. When a template exposes only one identifier with its own name, it's as if the code inside the template got instantiated right there. So tuplify!f is a function. It could have been a function template, or any other D construct. I used "alias tuplify!f tf;" to be able to use tf as ... an alias (doh!) to tuplify!f. But I could also have used: auto tf = &(tuplify!f); // tuplify!f is a function, tf is a pointer to this function. You can treat tf as any other D function : pass it around, give it as a argument to other templates or functions, etc. The template constraint if(isCallable!fun) means tuplify can only be instantiated on callables (function, delegates, pointer to functions, structs or classes with opCall defined, etc). Note that tuplify works on _all_ callables! After all, the only property I use in the code is that f can be called with (). isCallable lives here: http://digitalmars.com/d/2.0/phobos/std_traits.html#isCallable I use the ReturnType and ParameterTypeTuple templates to do a little compile-time introspection You can find ReturnType and ParameterTypeTuple here: http://digitalmars.com/d/2.0/phobos/std_traits.html#ReturnType So the internally created function accepts a Tuple!(the types fun needs) as argument. I indicate to the compiler it will return what fun returns, but I could have used auto there instead. Philippe
Sep 18 2010
parent reply Juanjo Alvarez <juanjux gmail.com> writes:
Philippe Sigaud wrote:


 1. Having the strings, defined at compile time, "MyClass" and "mymethod",
 how could I could I get to a delegate to MyClass.mymethod?
You can insert them in a piece of code as a string, and then mix it in: enum string code = "auto deleg = &" ~ className ~ "." ~ methodName ~ ";"; // auto deleg = &MyClass.mymethod; mixin(code); // created deleg, you're good to go.
I tried it and worked like a charm (but I've changed my code so instead of two strings to class + method, a fully qualified non member function address is used so I have a little more flexibility).
 2. Is there any way to apply a tuple to a function, expanding as
 arguments?
 Use the .field or .expand alias, exposed by Tuple!() to get a direct
 access to the internal expression tuple.
 
 f(tup.expand); // cracks tup open
Now, that is exactly what I wanted. It even works if the function has aditional arguments before the expanded tuple one's: // void f(int a, int b, double c) { writeln(a); writeln(b); writeln(c); } auto tup = tuple(42, 3.14); f(1, tup.expand); // Wonderful. I wish it was better documented on http://digitalmars.com/d/2.0/phobos/std_typecons.html
 As for expanding tuples inside functions if you do that regularly, I
 suggest you use a function adaptor, like this:
 
 template tuplify(alias fun) if (isCallable!fun)
 {
     ReturnType!fun tuplify(Tuple!(ParameterTypeTuple!fun) tup)
     {
         return fun(tup.expand);
     }
 }
Nice to know. I got you excellent explanation. Thanks * 100, Juanjo
Sep 18 2010
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sun, Sep 19, 2010 at 03:43, Juanjo Alvarez <juanjux gmail.com> wrote:

 enum string code = "auto deleg = &" ~ className ~ "." ~ methodName ~ ";";
 // auto deleg = &MyClass.mymethod;
 mixin(code); // created deleg, you're good to go.
I tried it and worked like a charm (but I've changed my code so instead of two strings to class + method, a fully qualified non member function address is used so I have a little more flexibility).
String mixins are quite powerful, if a bit clunky at times. See also __traits(getMember, ...) http://digitalmars.com/d/2.0/traits.html (look for getMember)
 Use the .field or .expand alias, exposed by Tuple!() to get a direct
 access to the internal expression tuple.

 f(tup.expand); // cracks tup open
Now, that is exactly what I wanted. It even works if the function has aditional arguments before the expanded tuple one's: // void f(int a, int b, double c) { writeln(a); writeln(b); writeln(c); }
Yes, because tuple.expand is directly the expression tuple stored inside Tuple!(...) When you have a typetuple in a template, like this: template List(T...) { } The T... part is a typetuple: an array of types, if you wish. You can get its length, index it, slice it, iterate on it, etc. You can have a variable with this strange 'type' and also get its length, etc. template List(T...) { T t; } t is an expression tuple. std.typecons.Tuple is nothing more than the preceding List!(...), with a few more functionalities. So, something interesting is that when you do tup.expand, you obtain a bunch of values of different types, all perfectly individually typed and usable. You can get one by indexing, slice it, etc. So, given your new f, you can even do: auto tup = tuple(42, 3.14); f(tup.expand[0], 1, tup.expand[1]); Though in this particular case, it's cleaner to do: f(tup[0], 1, tup[1]); Wonderful. I wish it was better documented on
 http://digitalmars.com/d/2.0/phobos/std_typecons.html
Yes, typecons.Tuple has lot of nifty things going for it, but there are not all documented. You can open a bug/enhancement request at http://d.puremagic.com/issues/
 Nice to know. I got you excellent explanation.

 Thanks * 100,
My pleasure, Philippe
Sep 18 2010
parent reply Juanjo Alvarez <juanjux gmail.com> writes:
Philippe Sigaud wrote:

 String mixins are quite powerful, if a bit clunky at times.
Both true, for what I'm seeing of them.
 See also __traits(getMember, ...)
 http://digitalmars.com/d/2.0/traits.html   (look for getMember)
Nice, with getMembers I could redesign again my test code for using objects instead of a virtual function, but I'll keep the current design because is simpler for me (I'm still adapting my brain to all this genericity). On my current implementation with functions I still have a doubt: I'm using template-functions like this one for my callbacks: void somefunc(T...) (string regex, T args...) { foreach (arg; args) { writeln(arg); } } Then, I write the "selector" for it (in other part of the program) like this: auto t = tuple("someregex", &(module.somefunc!(int, double)),tuple(2,3.14)); And then in a third module, I get those selectors and bind them to the functions like: auto selectable = t.field[1]; selectable(t.field[0], t.field[2].expand); // Called! With your help, this works like a charm. But sometimes there will be no variable arguments to "somefunc" (only the first string), and in that case I don't know what would be the correct syntax to specify that the variable number of arguments will have no args, both for the template instantiation and for the tuple: auto t = tuple("bla", &(module.somefunc!(void), tuple(void)); // Fails auto t = tuple("bla", &(module.somefunc!(void, Tuple!(void)()); // Fails too
 Yes, typecons.Tuple has lot of nifty things going for it, but there are
 not all documented.
 You can open a bug/enhancement request at http://d.puremagic.com/issues/
I'll do it. I guess currently most of you here check the source before the documentation since the former looks to be pretty outdated sometimes.
Sep 19 2010
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
 instead of a virtual function, but I'll keep the current design because is
 simpler for me (I'm still adapting my brain to all this genericity).
What languages are you used to? You seem to do quite well with genericity :)
 On my current implementation with functions I still have a doubt:

 I'm using template-functions like this one for my callbacks:

 void somefunc(T...) (string regex, T args...) {
    foreach (arg; args) {
        writeln(arg);
    }
 }
OK, no need for the last "..." args is already variadic. What you wrote is equivalent "I want any number of lists of any number of types". Just use: void somefunc(T...)(string regex, T args) { ...
 Then, I write the "selector" for it (in other part of the program) like
 this:

 auto t = tuple("someregex", &(module.somefunc!(int,
 double)),tuple(2,3.14));
A tuple in a tuple, nice.
 And then in a third module, I get those selectors and bind them to the
 functions like:

 auto selectable = t.field[1];
 selectable(t.field[0], t.field[2].expand); // Called!

 With your help, this works like a charm.

 But sometimes there will be no variable arguments to "somefunc" (only the
 first string), and in that case I don't know what would be the correct
 syntax to specify that the variable number of arguments will have no args,
 both for the template instantiation and for the tuple:

 auto t = tuple("bla", &(module.somefunc!(void), tuple(void)); // Fails
 auto t = tuple("bla", &(module.somefunc!(void, Tuple!(void)()); // Fails
 too
Use tuple(). Or Tuple!()() Another possibility could be to write your selector like this: auto t = tuple("bla", &(someFun), 2, 3.14); That is, the values are not wrapped in a tuple, but directly stored in t. That way, if you want no value, just do: auto t = tuple("bla", &(someFun)); // there is an expression tuple of length 0 there. auto selectable = t[1]; selectable(t[0], t.expand[2..$]); for now, you cannot directly get a slice from the tuple. You either use expand (an expression tuple, so slicable) or use the Tuple.slice member function (never used it). As to the difference between Tuple!(void) and Tuple!(): void is a 'valid' type (sort of). It's just a type that has no value. So Tuple!(void) and Tuple!() are different. You can define the type Tuple!(int, void, int), but you cannot create a value of that type, because the void field cannot have a value. Tuple!() is a tuple with no field, an empty tuple. You can create it all right. It's just, its length is zero, and it has no field. If you open it with expand, you get a zero-length typetuple. It's like an array of length zero: it's valid, but you cannot access its internal values, because there is none. Other programming languages call this the Unit type, because it's a (the) type that has only _one_ value, namely Tuple!()(). If you ask for the parameter typetuple for a no-arg function, you'll get a zero-length typetuple: int foo() { return 0;} alias ParameterTypeTuple!foo Pfoo; assert(is(Pfoo == TypeTuple!()); Pfoo pfoo; pfoo is a tuple of length 0. auto a = foo(pfoo); // we can pass it to a function with no arg. Philippe
Sep 19 2010
parent reply Juanjo Alvarez <juanjux gmail.com> writes:
Philippe Sigaud wrote:

 What languages are you used to? You seem to do quite well with genericity
 :)
What I've done profesionally and personally in the last year would be 90% Python, 5% Java and 5% C++. So yes, since Python is like a D with an uberauto and everything being runtime templates the real problem I have is with generic declarations :)
 OK, no need for the last "..."  args is already variadic. What you wrote
 is
 equivalent  "I want any number of lists of any number of types".
 Just use:
 
 void somefunc(T...)(string regex, T args) {
Changued!
 auto t = tuple("someregex", &(module.somefunc!(int,
 double)),tuple(2,3.14));
A tuple in a tuple, nice.
And maybe a tuple in a tuple in a tuple :) (see my other question later)
 auto t = tuple("bla", &(someFun), 2, 3.14);
 
 That is, the values are not wrapped in a tuple, but directly stored in t.
 That way, if you want no value, just do:
 auto t = tuple("bla", &(someFun)); // there is an expression tuple of
 length 0 there.
 
 auto selectable = t[1];
 selectable(t[0], t.expand[2..$]);
This is much better, no doubt. I've changued that part too (I'm really learning a lot with your posts, you should think about writing a book about D too). Now I've two new roadblocks: Roadblock1: If the tuple is defined in the same file as the code expanding it (with the template function in another file), it works. But if I move the tuple definition to another file (urls.d) the compiler gives me the error: Error: Unions with overlapping fields are not yet supported in CTFE urls.d(9): Error: cannot evaluate tuple("^/home/$",& index,42,3.14) at compile time urls.d(9): Error: cannot evaluate tuple("^/home/$",& index,42,3.14) at compile time That is, this works: // file: bindselector.d import std.typecons; import views; void main() { // tuple defined here auto selector_data = tuple( "^/home/$", &(views.index!(int, double)), 42, 3.14 ); auto sel_regex_var = selector_data.field[0]; auto sel_view_var = selector_data.field[1]; sel_view_var(sel_regex_var, selector_data.expand[2..$]); } But moving the declaration of "selector_data" to a file urls.d and then importing that file from bindselector.d gives me that error... bug? Roadblock2: The next step is to define multiple tuples in urls.d inside some iterable data structure, so bindselector.d can import that and do its thing inside a foreach. The problem here is related again with my limitations declaring generics. Since every tuple will have a different type signature, I tought that the logical structure to group them would be... yes, another tuple :) Then I would foreach on that structure and bind the params, etc. First try (Fail-1): // ---------------------------------------- auto selector_data_tuples = tuple( tuple( "^/home/$", &(views.index!(int, double)), 42, 3.14 ) ); foreach(selector_data; selector_data_tuples) { auto sel_regex_var = selector_data.field[0]; auto sel_view_var = selector_data.field[1]; sel_view_var(sel_regex_var, selector_data.expand[2..$]); } // ---------------------------------------- Compile error: Error: cannot infer type for selector_data Logical, since the type of the iterated element in the foreach is fixed, and I'm iterating over tuples of different types. Then I did (Fail-2): // ----------------------------------------------------------- for (int i = 0; i < selector_data_tuples.length; i++) { auto selector_data = selector_data_tuples.field[i]; auto sel_regex_var = selector_data.field[0]; auto sel_view_var = selector_data.field[1]; sel_view_var(sel_regex_var, selector_data.expand[2..$]); } // ----------------------------------------------------------- Which fails with a "Error: Integer constant expression expected instead of cast(uint)i". Is there any other way to iterate over a tuple with values of different tuples? Does d provide any other data structures better suited for this case? (other than an array of Object and a gazillion ugly casts).
Sep 19 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Juanjo Alvarez:

 But if I move the tuple 
 definition to another file (urls.d) the compiler gives me the error:
It seems a compiler bug. If not already present it probably needs to be added.
 Is there any other way to iterate over a tuple with values of different tuples?
You may use a typetuple, but those have to be used with lot of care, because the foreach on them is static, so I don't think that's what you want. D is statically typed, so you can't use it a dynamic typed language, as you are trying to do now.
 Does d provide any other data structures better suited for this 
 case? (other than an array of Object and a gazillion ugly casts). 
You may need something like an array of variants. Or to redesign your solution. Bye, bearophile
Sep 19 2010
prev sibling parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sun, Sep 19, 2010 at 18:33, Juanjo Alvarez <juanjux gmail.com> wrote:

 Philippe Sigaud wrote:

 What languages are you used to? You seem to do quite well with genericity
 :)
What I've done profesionally and personally in the last year would be 90% Python, 5% Java and 5% C++. So yes, since Python is like a D with an uberauto and everything being runtime templates the real problem I have is with generic declarations :)
OK, that's why you're using tuples for everything :) Python's tuples are cool. I think I *learnt* what tuples were while dabbling in Python a few years ago. (Haskell's ones are great too). Note that the 'real' D tuples are expression tuples (instantiated typetuples). The "T..." thingies in templates, once instantiated with T t; They know their length, are indexable, slicable, iterable, etc. In a way they are random-access range with a length, only with heterogeneous types. But I don't think they were conceived as tuples in other languages, not at first. I'd guess that, for Walter, they were just a natural extension/cleaning for templates, based on C++. But they are like Python's tuples! Except for one blatant limitation: they cannot be returned from a function :( But you can return a templated struct. So the basic idea is to wrap them in Tuple(T...) to be able to return them. Then std.typecons.tuple does its best to offer a interesting access to the underlying expression tuple. But it's not a first-class citizen in D: you cannot iterate them nor slice them... For now, because the situation has improved. What many people coming from C++/Java do is putting their stuff in structs/classes. They tend not to use tuples that much. This is much better, no doubt. I've changued that part too (I'm really
 learning a lot with your posts, you should think about writing a book about
 D too).
What I should do is moving my a** and have some code reviewed for Phobos. But it seems I can't find the time. When I've half an hour, I try to help here instead :)
 Now I've two new roadblocks:

 Roadblock1:

 If the tuple is defined in the same file as the code expanding it (with the
 template function in another file), it works. But if I move the tuple
 definition to another file (urls.d) the compiler gives me the error:

 Error: Unions with overlapping fields are not yet supported in CTFE
 urls.d(9): Error: cannot evaluate tuple("^/home/$",& index,42,3.14) at
 compile time
 urls.d(9): Error: cannot evaluate tuple("^/home/$",& index,42,3.14) at
 compile time

 That is, this works:

 // file: bindselector.d
 import std.typecons;
 import views;

 void main() {
     // tuple defined here
     auto selector_data = tuple( "^/home/$", &(views.index!(int, double)),
 42, 3.14 );
     auto sel_regex_var  = selector_data.field[0];
     auto sel_view_var = selector_data.field[1];
     sel_view_var(sel_regex_var, selector_data.expand[2..$]);
 }


 But moving the declaration of "selector_data" to a file urls.d and then
 importing that file from bindselector.d gives me that error... bug?
What do you mean, you moved the declaration of selector_data ? You declared it outside main() in another module? I don't know why, I never declare date outside functions/objects. Maybe if you declare it in the 'root scope' of the module (I don't know how to call that: the basic scope of a module, the one where the module name; is), it's initialized at compile time. And at compile time, I'm not sure things like addresses &(...) have a sense. Did you try to make selector_data a function? auto selector_data() { return tuple("^/home/$", &(views.index!(int, double)),42, 3.14 );}
 Roadblock2:
 The next step is to define multiple tuples in urls.d inside some iterable
 data structure, so bindselector.d can import that and do its thing inside a
 foreach.

 The problem here is related again with my limitations declaring generics.
 Since every tuple will have a different type signature, I tought that the
 logical structure to group them would be... yes, another tuple :) Then I
 would foreach on that structure and bind the params, etc.

 First try (Fail-1):

 // ----------------------------------------
 auto selector_data_tuples = tuple(
                                   tuple( "^/home/$", &(views.index!(int,
 double)), 42, 3.14 )
                                 );

 foreach(selector_data; selector_data_tuples) {
     auto sel_regex_var  = selector_data.field[0];
     auto sel_view_var = selector_data.field[1];
     sel_view_var(sel_regex_var, selector_data.expand[2..$]);
 }
 // ----------------------------------------

 Compile error: Error: cannot infer type for selector_data

 Logical, since the type of the iterated element in the foreach is fixed,
 and
 I'm iterating over tuples of different types.
Oh, but you can iterate on an expression tuple alright, even when all its elements have different types. I don't know why bearophile doesn't like that. It's just, as I've said higher up in this message, that tuple()/Tuple!() is a struct, not a 'real' tuple: it offers a nice access to the wrapped expression tuple, but not as much as you'd like. Use .expand to get direct access: auto t = tuple(1, 3.14, "abc"); foreach(i, field; t.expand) // get index and value { %s.",i,typeof(field).stringof, to!string(field)); } If you want to do some type sorcery, you can also iterate on the types. You can get access to them by the .Types alias in Tuple!(). foreach(i,Type; t.Types) // will iterate on (int,double,string) and not on (1, 3.14, "abc") {...} Philippe
Sep 20 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Philippe Sigaud:
 Oh, but you can iterate on an expression tuple alright, even when all its
 elements have different types. I don't know why bearophile doesn't like that.
I like the iteration on typetuples (the nomenclature here is a mess, see bug 4113), but you have to use such iteration with care because it's a static foreach, your code may become very long (this is why in bug 4085 I have asked to require a "static foreach" to iterate on typetuples, to help the programmer see the difference better). Bye, bearophile
Sep 20 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Philippe Sigaud:
 Recently, bug 2800 was corrected and you can also directly access a tuple's
 fields by indexing:
 double d = tup[1];  // or simply:     auto d = tup[1];
This program doesn't work: import std.typecons; void main() { auto t = tuple(42, 3.14); double d = t[1]; } And I think bug 2800 is unrelated: http://d.puremagic.com/issues/show_bug.cgi?id=2800 Bye, bearophile
Sep 19 2010
parent bearophile <bearophileHUGS lycos.com> writes:
 This program doesn't work:
 
 import std.typecons;
 void main() {
     auto t = tuple(42, 3.14);
     double d = t[1];
 }
 
 
 And I think bug 2800 is unrelated:
 http://d.puremagic.com/issues/show_bug.cgi?id=2800
But now I have seen this: http://www.dsource.org/projects/phobos/changeset/2018 Sorry, bye, bearophile
Sep 19 2010