Jump to page: 1 2
Thread overview
Templates - What's Up with the template keyword?
Apr 09
Alex
April 08
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.

April 08
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.)
April 08
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.


April 08
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.



April 08
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!
April 08
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.


April 09
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.
April 09
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();





April 09
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).



April 09
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.


« First   ‹ Prev
1 2