www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Templates - What's Up with the template keyword?

reply Ron Tarrant <rontarrant gmail.com> writes:
I'm digging into templates in an attempt to understand the 
signals-n-slots replacement for the observer pattern, but I've 
got a question I can't seem to find an answer for and an example 
for which I'm unable to solve the error.

First, the question...

In Michael Parker's book, "Learning D," (Packt, 2015) on page 160 
he gives an example of a basic template:

template MyTemplate(T)
{
    T val;

    void printVal()
    {
       import std.stdio : writeln;
       writeln("The type is ", typeid(T));
       writeln("The value is ", val);
    }
}

But in "Programming in D," (self, 2009-2018) by Ali Çehreli, 
there's no mention of the 'template' keyword in any of his 
examples.

Has the 'template' keyword been deprecated? Or is it optional?


And now, my broken code...

In Ali's book (section 64.7, page 401) this example is given:

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);
}

But I'm having trouble working out how this would be used. Up to 
this point, either the usage case is given or I was able to work 
it out on my own, but this one seems to demand a use case outside 
the scope of what's been covered in the chapter so far and I'm 
lost.

My full code for this example:

-----------------------------------------
import std.stdio;
import std.math;
import std.string;

struct Point(T)
{
	T x;
	T y;
	
	T distanceTo(Point that) const
	{
		immutable real xDistance = x - that.x;
		immutable real yDistance = y - that.y;
		
		immutable distance = sqrt((xDistance * xDistance) + (yDistance 
* yDistance));
		
		return(cast(T)distance);
		
	} // distanceTo()

} // struct Point


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));
	
} // getResponse() Point!T


void main()
{
	auto wayPoint1 = getResponse!Point("Where is the first map 
location?");
	auto wayPoint2 = getResponse!Point("Where is the second map 
location?");
	
	writeln("Distance: ", wayPoint1.distanceTo(wayPoint2));

} // main()

--------------------------------------
(lines 47 & 48) Error: template instance `getResponse!(Point)` 
does not match template declaration getResponse(T : 
Point!T)(string question)

Any help will be very much appreciated.
Apr 08 2019
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 8 April 2019 at 12:23:28 UTC, Ron Tarrant wrote:
 But in "Programming in D," (self, 2009-2018) by Ali Çehreli, 
 there's no mention of the 'template' keyword in any of his 
 examples.
You don't need template keyword for the majority of cases because the compiler lets you do shortcuts. If you write void foo(T)(int a) {} the compiler sees that as a shortcut of writing template foo(T) { void foo(int a) {} } and does all the necessary expansion for you. Writing long-form templates is for when you need to do it more advanced topics. Like a template is allowed to have several members if you write it long-form. You can also use it to force a specific strategy like CTFE inside the template (though the compiler now has shortcuts for that too, so you don't see it written long-form much more in new code).
 Point!T getResponse(T : Point!T)(string question)
This looks wrong... getResponse(T : Point) should work (and is what I see in the book), but `T : Point!T` is different. So what the colon does is specialize the template - it says "if you pass a type matching `Point` (the stuff after the colon), use this function instead of the generic function". The reason `T : Point!T` isn't compiling is that that's saying only match a recursive template... and you don't have one of those. You probably are thinking something along the lines of Point!T getResponse(T : Point!R, R)(string question) {} (maybe I am looking at the wrong part of the book, it is hard to find the right section/page number online). This is one of the most advanced template forms which is able to deconstruct other templates. So, if you call that with a Point!int p; getResponse!(typeof(p))("my question") then it will match T as being some kind of `Point!R`... and it will take apart the R into its own argument. So when given Point!int, it will see the part after ! happens to be int, and make R == int. Thus, inside the getResponse function: T == Point!int R == int and you can inspect that and react accordingly. Or you can ignore the R and just use the Point. The value here is you could pass Point!int or Point!float or Point!whatever all to the one function. (You can also do this if you just use an unrestricted T argument, but the Point!R specialization will make the intent more clear in documentation and lets you take it apart if you need to know what R is.)
Apr 08 2019
parent reply Ron Tarrant <rontarrant gmail.com> writes:
On Monday, 8 April 2019 at 12:40:10 UTC, Adam D. Ruppe wrote:

 You don't need template keyword for the majority of cases 
 because the compiler lets you do shortcuts.
Thanks, Adam. Good to know.
 (maybe I am looking at the wrong part of the book, it is hard 
 to find the right section/page number online).
My bad. The edition I'm working from was released 2018-10-17 and can be found here: http://ddili.org/ders/d.en/Programming_in_D.pdf The page number in online PDF viewer is 421 and the example is about 1/3 of the way down the page.
Apr 08 2019
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 04/08/2019 05:59 AM, Ron Tarrant wrote:
 On Monday, 8 April 2019 at 12:40:10 UTC, Adam D. Ruppe wrote:
 
 You don't need template keyword for the majority of cases because the 
 compiler lets you do shortcuts.
Thanks, Adam. Good to know.
 (maybe I am looking at the wrong part of the book, it is hard to find 
 the right section/page number online).
My bad. The edition I'm working from was released 2018-10-17 and can be found here: http://ddili.org/ders/d.en/Programming_in_D.pdf The page number in online PDF viewer is 421 and the example is about 1/3 of the way down the page.
That template specialization works along with the general definition of the same template. So, you must have the following function template as well: T getResponse(T)(string question) { writef("%s (%s): ", question, T.stringof); T response; readf(" %s", &response); return response; } As Steve said, when you replace all Ts in there e.g. with int, you get what the compiler would generate for T being int. However, the readf above cannot work with e.g. Point!double because readf does not know what Point!double is. For that reason, we may use a specialization for Point!T: 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)); } // getResponse() Point!T (It may be made to work with Adam's (T : Point!R, R) syntax but I failed just now.) Now you have a specialization that works with any Point!T. T can be double, int, etc. However, you must call getResponse with that special Point. I'm using an alias for code reduction: alias MapLocation = Point!double; auto wayPoint1 = getResponse!MapLocation("Where is the first map location?"); auto wayPoint2 = getResponse!MapLocation("Where is the second map location?"); Your code did not work because Point is not a type but a type template. (On the other hand, Point!double is a type). The whole program: import std.stdio; import std.math; import std.string; struct Point(T) { T x; T y; T distanceTo(Point that) const { immutable real xDistance = x - that.x; immutable real yDistance = y - that.y; immutable distance = sqrt((xDistance * xDistance) + (yDistance * yDistance)); return(cast(T)distance); } // distanceTo() } // struct Point 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)); } // getResponse() Point!T void main() { alias MapLocation = Point!double; auto wayPoint1 = getResponse!MapLocation("Where is the first map location?"); auto wayPoint2 = getResponse!MapLocation("Where is the second map location?"); writeln("Distance: ", wayPoint1.distanceTo(wayPoint2)); } // main() Ali
Apr 09 2019
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 10 April 2019 at 00:42:11 UTC, Ali Çehreli wrote:
 (It may be made to work with Adam's (T : Point!R, R) syntax but 
 I failed just now.)
You know, I didn't think T : Point!T would work, but it does. Huh. Anyway, the , R one works similarly, observe: --- T getResponse(T : Point!R, R)(string question) { writef("%s (%s): ", question, T.stringof); R response; readf(" %s", &response); return T(response); } --- In this case, when you pass it a Point!double, T is still the full type, Point!double (so the return value there needs to be constructed), but R is now just the `double` part, so we can pass that independently to readf. This pattern is also useful for capturing static arrays: void processStaticArray(A : T[N], T, size_t N)(ref A) { writeln(T.stringof, " ", N); } void main() { int[5] arr; processStaticArray(arr); } There, A is our static array type, but then we deconstruct its pieces to get the size and inner type too. This lets us pass any static array in without losing the compile-time length. (though usually I'd just slice it and call it T[]!) These patterns are also used by the `is` expression, and you can rename the whole thing there when used inside `static if`. (I know most people think this is crazy black magic, but it actually isn't that crazy once you know the pattern - write the symbol with placeholders, add a comma, define the placeholders. Then the other forms like is(T == X) and is(T : X) and even is(T N == X) are just optional additions to this - == means is equal to, : means can convert to, and the N in the last one renames the value extracted to something else, most valuable in is(typeof(func) Params == __parameters). So yeah, there is a bit to it, but it is just a pattern, condition, optional placeholders, optional rename.)
Apr 09 2019
prev sibling parent Ron Tarrant <rontarrant gmail.com> writes:
On Wednesday, 10 April 2019 at 00:42:11 UTC, Ali Çehreli wrote:
 Your code did not work because Point is not a type but a type 
 template. (On the other hand, Point!double is a type). The 
 whole program:
Thanks very much, Ali! Obviously, I've got some studying ahead of me.
Apr 10 2019
prev sibling next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Monday, 8 April 2019 at 12:23:28 UTC, Ron Tarrant wrote:

 First, the question...

 In Michael Parker's book, "Learning D," (Packt, 2015) on page 
 160 he gives an example of a basic template:

 template MyTemplate(T)
 {
    T val;

    void printVal()
    {
       import std.stdio : writeln;
       writeln("The type is ", typeid(T));
       writeln("The value is ", val);
    }
 }

 But in "Programming in D," (self, 2009-2018) by Ali Çehreli, 
 there's no mention of the 'template' keyword in any of his 
 examples.

 Has the 'template' keyword been deprecated? Or is it optional?
You should have read further along in that chapter :-) I evolve the MyTemplate example through the end of that "Templates as code blocks" section to this: template MyTemplate(T) { struct ValWrapper { T val; void printVal() { import std.stdio : writeln; writeln("The type is ", typeid(T)); writeln("The value is ", val); } } } And show that it must be instantiated like this: void main() { MyTemplate!int.ValWrapper vw1; MyTemplate!int.ValWrapper vw2; vw1.val = 20; vw2.val = 30; vw1.printVal(); vw2.printVal(); } And in the next section, "Struct and class templates", I introduce the concept of eponymous templates by rewriting MyTemplate like so: template ValWrapper(T) { struct ValWrapper { T val; void printVal() { writeln("The type is ", typeid(T)); writeln("The value is ", val); } } } And show that it can be instantiated with the shorthand: ValWrapper!int vw; And that the template can be refactored to this (since it's eponymous): struct ValWrapper(T) { T val; void printVal() { writeln("The type is ", typeid(T)); writeln("The value is ", val); } } In the subsequent sections, I show both long and short (eponymous) forms of enum and function templates.
Apr 08 2019
next sibling parent Ron Tarrant <rontarrant gmail.com> writes:
On Monday, 8 April 2019 at 14:56:46 UTC, Mike Parker wrote:

 You should have read further along in that chapter :-)
LOL! Actually, after reading Adam's reply, I dug back into your book and I'm starting to get a reasonable handle on this. I must say, I like the slow-but-steady intro you wrote. Thanks, Mike. Great book!
Apr 08 2019
prev sibling next sibling parent Ron Tarrant <rontarrant gmail.com> writes:
On Monday, 8 April 2019 at 14:56:46 UTC, Mike Parker wrote:

 In the subsequent sections, I show both long and short 
 (eponymous) forms of enum and function templates.
Forgot to say... I'm typing in the examples as I go and so far I haven't been lost. Even when you don't come right out and say how to use a particular template example, I'm able to fill in the blanks and get a complete, working example.
Apr 08 2019
prev sibling parent reply Ron Tarrant <rontarrant gmail.com> writes:
On Monday, 8 April 2019 at 14:56:46 UTC, Mike Parker wrote:

 In the subsequent sections, I show both long and short 
 (eponymous) forms of enum and function templates.
In your book, Mike, you stated:
 Remember, a template is only instantiated once for each set of 
 arguments and
 the same instantiation can be repeated in multiple modules 
 throughout a
 program. If each instantiation were scoped locally, the 
 template would no
 longer work as expected.
That leads me to speculate that it should be possible to use a class template as a sort of singleton. But, because a class template (from what I understand currently) needs to be instantiated using 'new,' I'm thinking maybe it isn't possible. Can you (or someone else) clear this up for me, please? What I have in mind is a template wrapped around a GTK AccelGroup that could then be instantiated in the MainWindow as well as inside any MenuItem needing a keyboard shortcut assignment.
Apr 09 2019
parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 9 April 2019 at 10:53:49 UTC, Ron Tarrant wrote:
 On Monday, 8 April 2019 at 14:56:46 UTC, Mike Parker wrote:

 In the subsequent sections, I show both long and short 
 (eponymous) forms of enum and function templates.
In your book, Mike, you stated:
 Remember, a template is only instantiated once for each set of 
 arguments and
 the same instantiation can be repeated in multiple modules 
 throughout a
 program. If each instantiation were scoped locally, the 
 template would no
 longer work as expected.
That leads me to speculate that it should be possible to use a class template as a sort of singleton. But, because a class template (from what I understand currently) needs to be instantiated using 'new,' I'm thinking maybe it isn't possible. Can you (or someone else) clear this up for me, please?
Instantiating a class template is not like instantiating a single instance of an object. What it does is create an implementation of the class, not an instance. So given this: class Foo(T) { T item; } Then this instantiation (which, byt the way, does not need new -- an alias will do the trick): alias IntFoo = Foo!int; Essentially does this: class FooWithInt { int item; } Of course, the symbol will be different than 'FooWithInt', but the point is for every T there has to be a uniquely named implementation of Foo.
 What I have in mind is a template wrapped around a GTK 
 AccelGroup that could then be instantiated in the MainWindow as 
 well as inside any MenuItem needing a keyboard shortcut 
 assignment.
Off the top of my head, to get a Singleton template, you could implement all of your singleton plumbing (thread safety if you need it, etc) in the template and add a `static _instance` member just as you would for any non-templated singleton: class Singleton(T) { private static Singleton!T _instance; static Singleton!T instance() { if(_instance is null) { _instance = new Singleton!T; } ... } private T _thing; ... } And you can then instantiate directly or, more conveniently, use an alias: alias Accelerators = Singleton!AccelGroup; Then: Accelerators.instance.doSomething();
Apr 09 2019
parent reply Ron Tarrant <rontarrant gmail.com> writes:
On Tuesday, 9 April 2019 at 14:25:18 UTC, Mike Parker wrote:

 Off the top of my head, to get a Singleton template, you could 
 implement all of your singleton plumbing (thread safety if you 
 need it, etc) in the template and add a `static _instance` 
 member just as you would for any non-templated singleton:

 class Singleton(T) {
     private static Singleton!T _instance;
     static Singleton!T instance() {
         if(_instance is null) {
             _instance = new Singleton!T;
         }
         ...
     }

     private T _thing;
     ...
 }

 And you can then instantiate directly or, more conveniently, 
 use an alias:

 alias Accelerators = Singleton!AccelGroup;

 Then:

 Accelerators.instance.doSomething();
So, I guess the short answer is 'no.' A template can't really substitute for a singleton without actually becoming a singleton in and of itself. I'm still struggling to understand templates, but I'll keep at it. Thanks, Mike.
Apr 09 2019
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/9/19 4:31 PM, Ron Tarrant wrote:

 I'm still struggling to understand templates, but I'll keep at it.
When I first saw C++ templates, I thought "what the hell is this black magic", and had no idea how they worked. In fact, this was back before STL, and I recall it was a set of templates called "Rogue Wave". None of us who were working on the project at the time understood them, so we ripped them all out. The thing that made it click for me is that a template is very akin to a macro substitution -- where you just copy and paste the given parameter wherever its substitute is found. For example: struct S(T) { T member; } S!int => put int wherever you see T. So it's like you typed: struct S { int member; } and that's what the compiler sees. All optimization, layout, etc. works exactly like you typed that directly. But instead of creating one type for every member type you need, you just write it once and "instantiate" the template giving the type you want. I will say, it's not EXACTLY the same as macro substitution -- there are rules. But it's a good way to think about it. I actually am going to go over this kind of thinking a bit in my talk this year at dconf. -Steve
Apr 09 2019
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Apr 09, 2019 at 04:48:45PM -0400, Steven Schveighoffer via
Digitalmars-d-learn wrote:
 On 4/9/19 4:31 PM, Ron Tarrant wrote:
 
 I'm still struggling to understand templates, but I'll keep at it.
[...]
 The thing that made it click for me is that a template is very akin to
 a macro substitution -- where you just copy and paste the given
 parameter wherever its substitute is found.
[...] Yes, templates are, functionally speaking, macros with type safety, and in D's case syntactic sanity, and none of the disadvantages of an actual macro system. T -- Heads I win, tails you lose.
Apr 09 2019
prev sibling parent Ron Tarrant <rontarrant gmail.com> writes:
On Tuesday, 9 April 2019 at 20:48:45 UTC, Steven Schveighoffer 
wrote:

 The thing that made it click for me is that a template is very 
 akin to a macro substitution -- where you just copy and paste 
 the given parameter wherever its substitute is found.
Nicely put. Thanks, Steve. I at least get the basics now.
Apr 10 2019
prev sibling next sibling parent reply Alex <AJ gmail.com> writes:
On Monday, 8 April 2019 at 12:23:28 UTC, Ron Tarrant wrote:
 I'm digging into templates in an attempt to understand the 
 signals-n-slots replacement for the observer pattern, but I've 
 got a question I can't seem to find an answer for and an 
 example for which I'm unable to solve the error.

 First, the question...
Your confusion arises in your understanding of meta programming and templates. Templates are compile time expressions that use parameters. They are exactly analogous to parameters in typical programming but allow one to pass around CT information that doesn't exist at run time. It is not the statement itself that is a template but the fact that it depends on a "template", which is a sort of abstract variable. class C(T) template C(T) struct(T) interface(T) void foo(T)() etc... are all things that depend on a template parameter T. One can say that they are all "templated" or are "templates". One can also say they are parameterized(meaning they take a parameter, but the parameters are CT "objects". Essentially template is the most general and the rest are just syntactic sugar. e.g., template CC(T) { class C { T t; } } template CC is a "template"(just like a cookie cutter but that can configure itself depending on what T is, as this example shows that we can create a class with different type of t. D has all kinda of stuff like this that help reduce the bloat of templates. Sometimes you must use one method over another for certain effects but usually you can pick and choose. The idea is not to think of meta programming as something different than normal programming. It is still programming and there is enough of an overlap that it benefits one to realize they are the same thing but are at different levels in the type system. Meta programming works on the type hierarchy while "runtime programming" works in the type hierarchy. Meta programming is a super set of runtime programming. The way I think of meta programming is that of coding runtime code. Run time code codes binary code. If I create a statement like writeln("test"); I know that the code gets translated in to a call instruction, pointers are used, "test" exists in some location in memory, etc... it all is machine code though when I compile. When I do something like this foo!int(); I know that the first part of foo!int is "meta code" and it first gets translated in to runtime code and then that code gets translated in to machine code. For example void foo(T)(T s) { writeln(s); } in this case, depending on what T is at compile time(which I get to decide in some way and so I know exactly what it is at compile time, in theory), foo takes on different versions. if I call foo(3) then it is writeln(3) and T is an int(by deduction in the compiler) and the compiler can even optimize out certain things here because it also knows that 3 is known.(this is CTFE) There is nothing to optimize in that example though. if I call foo(readln()) then the the compiler cannot optimize out but this code get translated in to writeln(readlin()) but T is a string(since readln returns a string and the compiler can deduce it). But in general foo acts as all these possibilities(and all the possibilities are known by the compiler because they have to be known to compile and use). So, what does templates do? They combine very similar code in to meta blocks that can then be easily used. They allow constructing very complex meta code such as void foo(T)(T s) { static if (is(T == string)) writeln(s); } and now foo is much more complicated because it has two completely different behaviors depending on what T is. You can't get that with run time code without a lot of work and it will never be efficient. But here the compiler will eventually know what T is and it can then choose the correct path at compile time. The if will disappear and so no extra overhead exists at runtime. So, templates are very powerful things(the above doesn't even dent their power) The idea to bear in mind is that anything in D is a template if it takes a template parameter. Think of a template as a drawing template used to sketch something. It isn't the final result but it shapes the final result. It is more like a blueprint or a program in and of itself. I think the hard part for many is that they can't balance the meta programming part with the run time part. This is very easy though if one always just keeps track of which side one is programming in and not to mix them up(mentally). The meta programming part will always be obvious, it will depend on template parameters and use meta programming constructs. There are sometimes overlap between the two levels but it because natural once one gets enough of an understanding. The hardest part about D is that it has so much meta programming stuff and there are sometimes bugs and special cases to do things, but the grammatical design of it's type system is very nice and sane compared to most other procedural programming languages. Always keep in mind that the templating system is about creating "generic" code. source code that generates source code(D could spit out a non-templated source code version if it was designed to) which then generates machine code. Each higher level allows one to organize levels below it and create generic solutions to problems. Ideally we would have even higher levels but programmers haven't evolved to this point yet(maybe some functional languages have).
Apr 09 2019
parent Ron Tarrant <rontarrant gmail.com> writes:
On Tuesday, 9 April 2019 at 14:41:30 UTC, Alex wrote:

 Your confusion arises in your understanding of meta programming 
 and templates. Templates are compile time expressions that use 
 parameters.
This sounds like another 'no.' :) Thanks for all the info, Alex.
Apr 09 2019
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 04/08/2019 05:23 AM, Ron Tarrant wrote:

 But in "Programming in D," (self, 2009-2018) by Ali Çehreli, there's no
 mention of the 'template' keyword in any of his examples.
'template' keyword is introduced here: http://ddili.org/ders/d.en/templates_more.html#ix_templates_more.template I found that page by clicking on one of the two 'template' entries under T in the book's index: http://ddili.org/ders/d.en/ix.html Ali
Apr 09 2019
parent reply Ron Tarrant <rontarrant gmail.com> writes:
On Wednesday, 10 April 2019 at 00:22:47 UTC, Ali Çehreli wrote:
 On 04/08/2019 05:23 AM, Ron Tarrant wrote:

 But in "Programming in D," (self, 2009-2018) by Ali Çehreli,
there's no
 mention of the 'template' keyword in any of his examples.
'template' keyword is introduced here: http://ddili.org/ders/d.en/templates_more.html#ix_templates_more.template I found that page by clicking on one of the two 'template' entries under T in the book's index: http://ddili.org/ders/d.en/ix.html Ali
Oops! Sorry, Ali. Since the advent of PDF, I rarely think to look in a book's index. I shall go blush now. And apologies for making such a blanket statement about not mentioning the template keyword. I've learned a lot from your book and I don't want to create the impression I haven't.
Apr 10 2019
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 04/10/2019 05:07 AM, Ron Tarrant wrote:

 the book's index:

   http://ddili.org/ders/d.en/ix.html
 Oops!
Not at all. One of the most useful parts of the book has been the index section for me. I occasionally search in there... like just yesterday for the positional format specifier %1$. :) Ali
Apr 10 2019