www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Can't understand templates

reply "Sly" <a a.com> writes:
Here is an example from the tutorial:

struct Point(T)
{
       T x;
       T y;
}

T getResponse(T)(string question) {
       writef("%s (%s): ", question, T.stringof);
       T response;
       readf(" %s", &response);
       return response;
}

Point!T getResponse(T: Point!T)(string question) {
       writefln("%s (Point!%s)", question, T.stringof);
       auto x = getResponse!T(" x");
       auto y = getResponse!T(" y");
       return Point!T(x, y);
}

void main()
{
       auto pt = getResponse!(Point!int)("point");
}


I don't understand how to read this signature: Point!T
getResponse(T: Point!T)(string question). Clearly it's not
supposed to be saying that T is subclass of Point!T, so those Ts
must refer to different types. But when I rename it to
getResponse(U: Point!T) I get a compilation error (unknown
identifier T).

Another question: say I have a template class Pair:

class Pair(A, B) {
       A a;
       B b;
       this(A a, B b) {this.a = a; this.b = b;}
}

How to write a specialization for getResponse matching Pair!(A,
B) with any type arguments A and B? I tried this:

T getResponse(T)(string question)
       if (is(T: Pair!(A, B), A, B))
{
       auto a = getResponse!A(" a");
       auto b = getResponse!B(" b");
       return new Pair!(A, B)(a, b);
}

but get a compile error:

error: d.getResponse called with argument types (string) matches
both:
	d.d(19): getResponse(T)(string question)
and:
	d.d(40): getResponse(T)(string question) if (is(T : Pair!(A, B),
A, B))
Nov 28 2014
parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Fri, Nov 28, 2014 at 07:32:38PM +0000, Sly via Digitalmars-d-learn wrote:
 Here is an example from the tutorial:
[...]
 Point!T getResponse(T: Point!T)(string question) {
       writefln("%s (Point!%s)", question, T.stringof);
       auto x = getResponse!T(" x");
       auto y = getResponse!T(" y");
       return Point!T(x, y);
 }
[...]
 I don't understand how to read this signature: Point!T
 getResponse(T: Point!T)(string question). Clearly it's not
 supposed to be saying that T is subclass of Point!T, so those Ts
 must refer to different types. But when I rename it to
 getResponse(U: Point!T) I get a compilation error (unknown
 identifier T).
This syntax is a little confusing, but basically the ":" there is saying "this type, when instantiated with the following pattern, produces a valid type". Essentially it's equivalent to: Point!T getResponse(T)(string question) if (is(typeof(Point!T))) // i.e., Point!T is a valid type { ... }
 Another question: say I have a template class Pair:
 
 class Pair(A, B) {
       A a;
       B b;
       this(A a, B b) {this.a = a; this.b = b;}
 }
 
 How to write a specialization for getResponse matching Pair!(A, B)
 with any type arguments A and B? I tried this:
 
 T getResponse(T)(string question)
       if (is(T: Pair!(A, B), A, B))
 {
       auto a = getResponse!A(" a");
       auto b = getResponse!B(" b");
       return new Pair!(A, B)(a, b);
 }
 
 but get a compile error:
 
 error: d.getResponse called with argument types (string) matches
 both:
 	d.d(19): getResponse(T)(string question)
 and:
 	d.d(40): getResponse(T)(string question) if (is(T : Pair!(A, B),
 A, B))
Yes, because D does not support overlapping template overloads. Both the original overload of getResponse and the new overload matches the given arguments, so it's an error. You need to explicitly restrict the original overload so that it does *not* match in the case a Pair is given. For example: // Original overload Point!T getResponse(T : Point!T)(string question) if (!is(T : Pair!(A, B), A, B)) // ^^^ this says, match all T's for which Point!T is a // valid type, but not when T is also an instantiation // of Pair!(A,B). { ... } // New overload T getResponse(T)(string question) if (is(T : Pair!(A, B), A, B)) { ... } T -- It is impossible to make anything foolproof because fools are so ingenious. -- Sammy
Nov 28 2014
parent reply "Sly" <a a.com> writes:
On Friday, 28 November 2014 at 19:45:48 UTC, H. S. Teoh via
Digitalmars-d-learn wrote:

 This syntax is a little confusing, but basically the ":" there 
 is saying
 "this type, when instantiated with the following pattern, 
 produces a
 valid type". Essentially it's equivalent to:

 	Point!T getResponse(T)(string question)
 		if (is(typeof(Point!T))) // i.e., Point!T is a valid type
 	{
 		...
 	}
Is there a typo here? This version doesn't compile with the original call: auto pt = getResponse!(Point!int)("point");
 Yes, because D does not support overlapping template overloads.
But I thought the original example has ovelapping templates. Let's take a simpler example from earlier in the tutorial, where Point is a non-template struct: // The general definition of the function template (same as before) T getResponse(T)(string question) { writef("%s (%s): ", question, T.stringof); T response; readf(" %s", &response); return response; } // The specialization of the function template for Point T getResponse(T : Point)(string question) { writefln("%s (Point)", question); auto x = getResponse!int(" x"); auto y = getResponse!int(" y"); return Point(x, y); } auto center = getResponse!Point("Where is the center?"); Doesn't getResponse!Point match both declarations?
Nov 28 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 11/28/2014 12:36 PM, Sly wrote:

 Let's take a simpler example from earlier in the tutorial, where
 Point is a non-template struct:


 // The general definition of the function template (same as
 before)
 T getResponse(T)(string question)
 {
[...]
 }
That definition would work with any type T. So, it is general: it accepts any type.
 // The specialization of the function template for Point
 T getResponse(T : Point)(string question)
 {
[...]
 }
That definition is for T that matches Point (i.e. Point itself in this example). So, it is special: it accepts only that type.
 auto center = getResponse!Point("Where is the center?");

 Doesn't getResponse!Point match both declarations?
True but the call is dispatched to the most special of all of the matches. It is very briefly explained through a reference to C++: "The template picked to instantiate is the one that is most specialized that fits the types of the TemplateArgumentList. Determine which is more specialized is done the same way as the C++ partial ordering rules. If the result is ambiguous, it is an error." See the "Specialization" heading here: http://dlang.org/template.html Ali
Nov 28 2014
parent reply "Sly" <a a.com> writes:
OK, so we *can* have overlapping templates in one fits better
than another, just like in C++. I still don't understand how to
read this signature:
Point!T getResponse(T: Point!T)(string question)


On Friday, 28 November 2014 at 23:59:07 UTC, Ali Çehreli wrote:
 On 11/28/2014 12:36 PM, Sly wrote:

 Let's take a simpler example from earlier in the tutorial,
where
 Point is a non-template struct:


 // The general definition of the function template (same as
 before)
 T getResponse(T)(string question)
 {
[...]
 }
That definition would work with any type T. So, it is general: it accepts any type.
 // The specialization of the function template for Point
 T getResponse(T : Point)(string question)
 {
[...]
 }
That definition is for T that matches Point (i.e. Point itself in this example). So, it is special: it accepts only that type.
 auto center = getResponse!Point("Where is the center?");

 Doesn't getResponse!Point match both declarations?
True but the call is dispatched to the most special of all of the matches. It is very briefly explained through a reference to C++: "The template picked to instantiate is the one that is most specialized that fits the types of the TemplateArgumentList. Determine which is more specialized is done the same way as the C++ partial ordering rules. If the result is ambiguous, it is an error." See the "Specialization" heading here: http://dlang.org/template.html Ali
Nov 28 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 11/28/2014 10:26 PM, Sly wrote:

 I still don't understand how to
 read this signature:
 Point!T getResponse(T: Point!T)(string question)
The following is what makes sense to me. Sorry for going step by step but I think it helped me understand your question. (See item 3 below.) 1) Point!T is the return type. Although T is unknown at this point, we will wait a little because it may be a template parameter. Also, according to the syntax of Point!T, Point is apparently a template. So, this function will return the T instantiation of Point, whatever T will be deduced to be in a moment. 2) getResponse is the name of the function 3) The first set of parameters is the template parameters: (T: Point!T) The meaning: T is a type and this specialization is for Point!T. Ok, I think I finally understand your original question. I agree that it doesn't make sense to say "T is a type and this specialization is for Point!T". I think we need to read it backward in this case: "This specialization is for Point!T. If so, deduce T from that." So, if getResponse is instantiated with Point!int, then T is deduced to be int. 4) The instances of this function template take string parameter. Ali
Nov 29 2014
parent reply "Sly" <a a.com> writes:
Ah, now I see. In C++ we would also need to supply template
parameters, but we'd put them before return type
(template<typename T> getResponse<Point<T>>); in D we put them
before the colon.

Now I'm trying to write a specialization for Pair. It seems that
this is the way to do it:

Pair!(A, B) getResponse(A, B: Pair!(A, B))(string question)
{
      auto a = getResponse!A(" a");
      auto b = getResponse!B(" b");
      return new Pair!(A, B)(a, b);
}

auto pair = getResponse!(Pair!(int, int))("pair?");

But it doesn't work: this call resolves to a general version T
getResponse(T), and I get a compilation error because it tries to
instantiate readf!(Pair*).
What am I doing wrong?

On Saturday, 29 November 2014 at 08:06:33 UTC, Ali Çehreli wrote:
 On 11/28/2014 10:26 PM, Sly wrote:

 I still don't understand how to
 read this signature:
 Point!T getResponse(T: Point!T)(string question)
The following is what makes sense to me. Sorry for going step by step but I think it helped me understand your question. (See item 3 below.) 1) Point!T is the return type. Although T is unknown at this point, we will wait a little because it may be a template parameter. Also, according to the syntax of Point!T, Point is apparently a template. So, this function will return the T instantiation of Point, whatever T will be deduced to be in a moment. 2) getResponse is the name of the function 3) The first set of parameters is the template parameters: (T: Point!T) The meaning: T is a type and this specialization is for Point!T. Ok, I think I finally understand your original question. I agree that it doesn't make sense to say "T is a type and this specialization is for Point!T". I think we need to read it backward in this case: "This specialization is for Point!T. If so, deduce T from that." So, if getResponse is instantiated with Point!int, then T is deduced to be int. 4) The instances of this function template take string parameter. Ali
Nov 29 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 11/29/2014 12:41 AM, Sly wrote:
 Ah, now I see. In C++ we would also need to supply template
 parameters, but we'd put them before return type
 (template<typename T> getResponse<Point<T>>); in D we put them
 before the colon.

 Now I'm trying to write a specialization for Pair. It seems that
 this is the way to do it:

 Pair!(A, B) getResponse(A, B: Pair!(A, B))(string question)
 {
       auto a = getResponse!A(" a");
       auto b = getResponse!B(" b");
       return new Pair!(A, B)(a, b);
 }

 auto pair = getResponse!(Pair!(int, int))("pair?");

 But it doesn't work: this call resolves to a general version T
 getResponse(T), and I get a compilation error because it tries to
 instantiate readf!(Pair*).
 What am I doing wrong?
I wish I could formalize these definitions better or consistently but here my "art", which works: :) Pair!(A, B) getResponse(P : Pair!(A, B), A, B)(string question) { // ... } That template parameter reads: - This template takes a single parameter (P) - P must match Pair!(A, B) - Where A and B are both types So, getting back to Point, now the following is consistent with the above: Point!T getResponse(P : Point!T, T)(string question) { // ... } Now, that means: "This is a specialization for P, where it matches Point!T and T is a type." Maybe I should change the specialization examples in that chapter... Ali
Nov 29 2014
parent reply "Sly" <a a.com> writes:
On Saturday, 29 November 2014 at 09:11:51 UTC, Ali Çehreli wrote:

 Point!T getResponse(P : Point!T, T)(string question)
 {
     // ...
 }
This doesn't work because now this definition has 2 parameters P and T. I have to specify both like this: auto pt = getResponse!(Point!int, int)("point"); which of course defeats the purpose. Otherwise I have ambiguity error: d.d:73: error: d.getResponse called with argument types (string) matches both: d.d(20): getResponse(T)(string question) and: d.d(31): getResponse(P : Point!T, T)(string question) Same thing with specialization for Pair. Actually what I wrote earlier about C++ is wrong because C++ doesn't allow partial specialization for functions. I wrote this example in C++ using classes: template<typename T> struct Reader { static T getResponse(const std::string& question) { std::cout << question << "(" << typeid(T).name() << "): "; T res; std::cin >> res; return res; } }; template<typename T> struct Point { T x, y; }; template<typename T> struct Reader<Point<T>> { static Point<T> getResponse(const std::string& question) { std::cout << question << "\n"; Point<T> res; res.x = Reader<T>::getResponse(" x"); res.y = Reader<T>::getResponse(" y"); return res; } }; template<typename A, typename B> struct Reader<std::pair<A, B>> { static std::pair<A, B> getResponse(const std::string& question) { std::cout << question << "\n"; std::pair<A, B> res; res.first = Reader<A>::getResponse(" first"); res.second = Reader<B>::getResponse(" second"); return res; } }; int main() { int i = Reader<int>::getResponse("int"); auto pt = Reader<Point<int>>::getResponse("point"); auto pair = Reader<std::pair<int, int>>::getResponse("pair"); return 0; } I translated it literally into D using your syntax and it worked! class Reader(T) { public static T getResponse(string question) { writef("%s (%s): ", question, T.stringof); T response; readf(" %s", &response); return response; } } class Reader(P: Point!T, T) { public static Point!T getResponse(string question) { writefln("%s (Point!%s)", question, T.stringof); auto x = Reader!T.getResponse(" x"); auto y = Reader!T.getResponse(" y"); return Point!T(x, y); } } class Reader(P: Pair!(A, B), A, B) { public static Pair!(A, B) getResponse(string question) { writefln("%s (Pair!(%s, %s))", question, A.stringof, B.stringof); auto a = Reader!A.getResponse(" a"); auto b = Reader!B.getResponse(" b"); return new Pair!(A, B)(a, b); } } void main() { auto i = Reader!int.getResponse("int"); auto pt = Reader!(Point!int).getResponse("point"); auto pair = Reader!(Pair!(int, int)).getResponse("pair"); } My questions now are: 1. Apparently D has partial specializations of function templates, but it uses a weird syntax (T: Point!T). How to extend this syntax for multiple template parameters? (Basically, how to write this example without wrapping the function into a class?) 2. Why is there a difference between partial specialization of function templates and class templates: there's a compilation error with function templates but not with class templates?
Nov 29 2014
parent reply "Meta" <jared771 gmail.com> writes:
On Saturday, 29 November 2014 at 11:07:34 UTC, Sly wrote:
 On Saturday, 29 November 2014 at 09:11:51 UTC, Ali Çehreli 
 wrote:

 Point!T getResponse(P : Point!T, T)(string question)
 {
    // ...
 }
This doesn't work because now this definition has 2 parameters P and T. I have to specify both like this: auto pt = getResponse!(Point!int, int)("point"); which of course defeats the purpose. Otherwise I have ambiguity error:
Not true, T is inferred based on the the type Point has been instantiated with, letting you emit the second template argument. The following code works: struct Point(T) { T x; T y; } P getResponse(P: Point!T, T)(string message) { return P(T.init, T.init); } void main() { import std.stdio; //T is inferred to be int automatically writeln(getResponse!(Point!int)("test")); }
Nov 29 2014
parent reply "Sly" <a a.com> writes:
You miss another definition which introduces a conflict:
T getResponse(T)(string question)
{...}

On Saturday, 29 November 2014 at 17:20:42 UTC, Meta wrote:
 On Saturday, 29 November 2014 at 11:07:34 UTC, Sly wrote:
 On Saturday, 29 November 2014 at 09:11:51 UTC, Ali Çehreli 
 wrote:

 Point!T getResponse(P : Point!T, T)(string question)
 {
   // ...
 }
This doesn't work because now this definition has 2 parameters P and T. I have to specify both like this: auto pt = getResponse!(Point!int, int)("point"); which of course defeats the purpose. Otherwise I have ambiguity error:
Not true, T is inferred based on the the type Point has been instantiated with, letting you emit the second template argument. The following code works: struct Point(T) { T x; T y; } P getResponse(P: Point!T, T)(string message) { return P(T.init, T.init); } void main() { import std.stdio; //T is inferred to be int automatically writeln(getResponse!(Point!int)("test")); }
Nov 29 2014
next sibling parent reply "Meta" <jared771 gmail.com> writes:
On Saturday, 29 November 2014 at 18:19:40 UTC, Sly wrote:
 You miss another definition which introduces a conflict:
 T getResponse(T)(string question)
 {...}
In that case, you're better off with a pair of declarations: struct Point(T) { T x; T y; } T getResponse(T)(string message) if (!is(T == Point!U, U)) { return T.init; } Point!T getResponse(T)(string message) if (is(T == Point!U, U)) { return Point!T(T.init, T.init); } void main() { auto t = getResponse!int("test"); auto p = getResponse!(Point!int)("test"); }
Nov 29 2014
parent "Sly" <a a.com> writes:
This is clearly an incorrect way because it requires editing the
original definition every time a new class is introduced.

On Saturday, 29 November 2014 at 19:10:34 UTC, Meta wrote:
 On Saturday, 29 November 2014 at 18:19:40 UTC, Sly wrote:
 You miss another definition which introduces a conflict:
 T getResponse(T)(string question)
 {...}
In that case, you're better off with a pair of declarations: struct Point(T) { T x; T y; } T getResponse(T)(string message) if (!is(T == Point!U, U)) { return T.init; } Point!T getResponse(T)(string message) if (is(T == Point!U, U)) { return Point!T(T.init, T.init); } void main() { auto t = getResponse!int("test"); auto p = getResponse!(Point!int)("test"); }
Nov 29 2014
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 11/29/2014 10:19 AM, Sly wrote:

 You miss another definition which introduces a conflict:
 T getResponse(T)(string question)
 {...}
The following works with dmd git head: import std.stdio; T getResponse(T)(string question) { writef("%s (%s): ", question, T.stringof); T response; readf(" %s", &response); return response; } class Pair(A, B) { A a; B b; this(A a, B b) {this.a = a; this.b = b;} } Pair!(A, B) getResponse(P : Pair!(A, B), A, B)(string question) { writeln(question); auto a = getResponse!A(" a"); auto b = getResponse!B(" b"); return new Pair!(A, B)(a, b); } struct Point(T) { T x; T y; } Point!T getResponse(P : Point!T, T)(string question) { writefln("%s (Point!%s)", question, T.stringof); auto x = getResponse!T(" x"); auto y = getResponse!T(" y"); return Point!T(x, y); } void main() { auto number = getResponse!int("number?"); writeln(number); auto pair = getResponse!(Pair!(int, long))("pair?"); writeln(pair); auto point = getResponse!(Point!int)("point?"); writeln(point); } Ali
Nov 29 2014
parent "Sly" <a a.com> writes:
OK. I'm using gdc 4.8.2 and this doesn't compile, so it's
probably an old version. Checked on ideone and it works with
dmd-2.042.
Thanks a lot for your help!

On Saturday, 29 November 2014 at 20:16:24 UTC, Ali Çehreli wrote:
 On 11/29/2014 10:19 AM, Sly wrote:

 You miss another definition which introduces a conflict:
 T getResponse(T)(string question)
 {...}
The following works with dmd git head: import std.stdio; T getResponse(T)(string question) { writef("%s (%s): ", question, T.stringof); T response; readf(" %s", &response); return response; } class Pair(A, B) { A a; B b; this(A a, B b) {this.a = a; this.b = b;} } Pair!(A, B) getResponse(P : Pair!(A, B), A, B)(string question) { writeln(question); auto a = getResponse!A(" a"); auto b = getResponse!B(" b"); return new Pair!(A, B)(a, b); } struct Point(T) { T x; T y; } Point!T getResponse(P : Point!T, T)(string question) { writefln("%s (Point!%s)", question, T.stringof); auto x = getResponse!T(" x"); auto y = getResponse!T(" y"); return Point!T(x, y); } void main() { auto number = getResponse!int("number?"); writeln(number); auto pair = getResponse!(Pair!(int, long))("pair?"); writeln(pair); auto point = getResponse!(Point!int)("point?"); writeln(point); } Ali
Nov 29 2014