digitalmars.D.learn - Can't understand templates
- Sly (50/50) Nov 28 2014 Here is an example from the tutorial:
- H. S. Teoh via Digitalmars-d-learn (34/74) Nov 28 2014 [...]
- Sly (26/37) Nov 28 2014 Is there a typo here? This version doesn't compile with the
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (16/29) Nov 28 2014 That definition would work with any type T. So, it is general: it
- Sly (5/40) Nov 28 2014 OK, so we *can* have overlapping templates in one fits better
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (20/23) Nov 29 2014 The following is what makes sense to me.
- Sly (18/44) Nov 29 2014 Ah, now I see. In C++ we would also need to supply template
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (20/37) Nov 29 2014 I wish I could formalize these definitions better or consistently but
- Sly (96/100) Nov 29 2014 This doesn't work because now this definition has 2 parameters P
- Meta (20/31) Nov 29 2014 Not true, T is inferred based on the the type Point has been
- Sly (4/38) Nov 29 2014 You miss another definition which introduces a conflict:
- Meta (22/25) Nov 29 2014 In that case, you're better off with a pair of declarations:
- Sly (3/28) Nov 29 2014 This is clearly an incorrect way because it requires editing the
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (44/47) Nov 29 2014 The following works with dmd git head:
- Sly (5/52) Nov 29 2014 OK. I'm using gdc 4.8.2 and this doesn't compile, so it's
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
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
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
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
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,wherePoint 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
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
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
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
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
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: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")); }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:
Nov 29 2014
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: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")); }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:
Nov 29 2014
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
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
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
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