www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Template question

reply Chad J <""gamerChad\" spamIsBad gmail.com"> writes:
I'm trying to write a template that, when instantiated, gives a value 
that is the same as the last time it was instantiated plus one.  Here is 
some code I tried, but it doesn't work:

import std.stdio;

template GetUID()
{
     const ulong accumulator;

     static if ( ++accumulator != 0 )
         const ulong GetUID = accumulator;
     else
         static assert(0);
}

void main()
{
     writefln( GetUID!() ); // should print 1
     writefln( GetUID!() ); // should print 2
}

Hopefully that gives a good idea of what I am shooting for.
Oct 04 2006
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Chad J > wrote:
 I'm trying to write a template that, when instantiated, gives a value 
 that is the same as the last time it was instantiated plus one.
I don't think this is possible. Once a template is evaluated that's it. Future references to a template do not cause a re-evaluation. Sean
Oct 04 2006
parent reply Chad J <""gamerChad\" spamIsBad gmail.com"> writes:
Sean Kelly wrote:
 Chad J > wrote:
 
 I'm trying to write a template that, when instantiated, gives a value 
 that is the same as the last time it was instantiated plus one.
I don't think this is possible. Once a template is evaluated that's it. Future references to a template do not cause a re-evaluation. Sean
Oh, bummer. Is there another way to do what I want to do at compile-time, besides just manually writing the numbers in?
Oct 04 2006
next sibling parent reply Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Chad J schrieb am 2006-10-04:
 Sean Kelly wrote:
 Chad J > wrote:
 
 I'm trying to write a template that, when instantiated, gives a value 
 that is the same as the last time it was instantiated plus one.
I don't think this is possible. Once a template is evaluated that's it. Future references to a template do not cause a re-evaluation. Sean
Oh, bummer. Is there another way to do what I want to do at compile-time, besides just manually writing the numbers in?
Here is a kludge(limitation: only UID instantiation per line and file allowed): Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFJF8MLK5blCcjpWoRArtDAJ42K2nEhKcEWNvpp3q+LjHVdBzAfwCfa8dT rS6sQyT7s1XUic1TLz3Y2+A= =+5nW -----END PGP SIGNATURE-----
Oct 04 2006
parent Chad J <""gamerChad\" spamIsBad gmail.com"> writes:
Thomas Kuehne wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Chad J schrieb am 2006-10-04:
 
Sean Kelly wrote:

Chad J > wrote:


I'm trying to write a template that, when instantiated, gives a value 
that is the same as the last time it was instantiated plus one.
I don't think this is possible. Once a template is evaluated that's it. Future references to a template do not cause a re-evaluation. Sean
Oh, bummer. Is there another way to do what I want to do at compile-time, besides just manually writing the numbers in?
Here is a kludge(limitation: only UID instantiation per line and file allowed): Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFJF8MLK5blCcjpWoRArtDAJ42K2nEhKcEWNvpp3q+LjHVdBzAfwCfa8dT rS6sQyT7s1XUic1TLz3Y2+A= =+5nW -----END PGP SIGNATURE-----
Maybe I'm wrong, but it seems like it can't guarantee uniqueness due to hashing of the files. Anyhow, it's a useful template to keep in mind. Thanks for that!
Oct 04 2006
prev sibling parent BCS <BCS pathlink.com> writes:
Chad J > wrote:
 Sean Kelly wrote:
 
 Chad J > wrote:

 I'm trying to write a template that, when instantiated, gives a value 
 that is the same as the last time it was instantiated plus one.
I don't think this is possible. Once a template is evaluated that's it. Future references to a template do not cause a re-evaluation. Sean
Oh, bummer. Is there another way to do what I want to do at compile-time, besides just manually writing the numbers in?
It would require a new feature but... <code> enum Root{} enum foo : Root{a} // a is distinct from all values in types deriving from Root foo.a; enum bar : Root{a} // a is distinct from all values in types deriving from Root bar.a </code> Implementing this would be a pain though (putting the enums in different files...) :P
Oct 04 2006
prev sibling next sibling parent reply Reiner Pope <reiner.pope REMOVE.THIS.gmail.com> writes:
Chad J > wrote:
 I'm trying to write a template that, when instantiated, gives a value 
 that is the same as the last time it was instantiated plus one.  Here is 
 some code I tried, but it doesn't work:
The reason this sort of thing is not supported with templates is that it creates ambiguities. Template evaluation is lazy, meaning (a) it is only done when it is required, and (b) it can be done in any order. This is really the obvious way to do it. For example, consider the following code: void foo() { writefln( GetUID!() ); } void bar() { writefln( GetUID!() ); } void main() { bar(); foo(); } Which one prints 1, and which one prints 2? I can think of two arbitrary rules which resolve the ambiguity: 1. evaluate all templates from the top of the source code down; or 2. evaluate all templates according to execution order. The problem is that 1. is arbitrary and contradicts the assumption and aim the rearranging the order of functions won't change anything, and 2. is not always decidable, vis a vis the halting problem. I don't actually have an idea of what you are trying to accomplish, though. If just simple accumulator, then write it as actual D code and let the optimizer inline and pre-evaluate it without your assistance. If you want something else, I'm sure there is a way to do it within the template system -- you'll probably find Thomas and BCS's suggestions the right sort of thing. Cheers, Reiner
Oct 04 2006
parent reply Chad J <""gamerChad\" spamIsBad gmail.com"> writes:
Reiner Pope wrote:
 Chad J > wrote:
 
 I'm trying to write a template that, when instantiated, gives a value 
 that is the same as the last time it was instantiated plus one.  Here 
 is some code I tried, but it doesn't work:
The reason this sort of thing is not supported with templates is that it creates ambiguities. Template evaluation is lazy, meaning (a) it is only done when it is required, and (b) it can be done in any order. This is really the obvious way to do it. For example, consider the following code: void foo() { writefln( GetUID!() ); } void bar() { writefln( GetUID!() ); } void main() { bar(); foo(); } Which one prints 1, and which one prints 2? I can think of two arbitrary rules which resolve the ambiguity: 1. evaluate all templates from the top of the source code down; or 2. evaluate all templates according to execution order. The problem is that 1. is arbitrary and contradicts the assumption and aim the rearranging the order of functions won't change anything, and 2. is not always decidable, vis a vis the halting problem. I don't actually have an idea of what you are trying to accomplish, though. If just simple accumulator, then write it as actual D code and let the optimizer inline and pre-evaluate it without your assistance. If you want something else, I'm sure there is a way to do it within the template system -- you'll probably find Thomas and BCS's suggestions the right sort of thing. Cheers, Reiner
I see. I was kind of expecting arbitrary order of evaluation, but didn't care really. This one was mostly just a learning excercise for me to get a better feel for templates. In a way it would be nice if there was some sort of tutorial type resource for D templates; hopefully something that doesn't rely on C++ background. For a long time I just ignored them because they were scary :) Anyhow, thanks for the info!
Oct 04 2006
parent Don Clugston <dac nospam.com.au> writes:
Chad J > wrote:
 Reiner Pope wrote:
 Chad J > wrote:

 I'm trying to write a template that, when instantiated, gives a value 
 that is the same as the last time it was instantiated plus one.  Here 
 is some code I tried, but it doesn't work:
The reason this sort of thing is not supported with templates is that it creates ambiguities. Template evaluation is lazy, meaning (a) it is only done when it is required, and (b) it can be done in any order. This is really the obvious way to do it. For example, consider the following code: void foo() { writefln( GetUID!() ); } void bar() { writefln( GetUID!() ); } void main() { bar(); foo(); } Which one prints 1, and which one prints 2? I can think of two arbitrary rules which resolve the ambiguity: 1. evaluate all templates from the top of the source code down; or 2. evaluate all templates according to execution order. The problem is that 1. is arbitrary and contradicts the assumption and aim the rearranging the order of functions won't change anything, and 2. is not always decidable, vis a vis the halting problem. I don't actually have an idea of what you are trying to accomplish, though. If just simple accumulator, then write it as actual D code and let the optimizer inline and pre-evaluate it without your assistance. If you want something else, I'm sure there is a way to do it within the template system -- you'll probably find Thomas and BCS's suggestions the right sort of thing. Cheers, Reiner
I see. I was kind of expecting arbitrary order of evaluation, but didn't care really. This one was mostly just a learning excercise for me to get a better feel for templates. In a way it would be nice if there was some sort of tutorial type resource for D templates; hopefully something that doesn't rely on C++ background. For a long time I just ignored them because they were scary :)
A tutorial would be nice. I started writing one a while ago, but then the language changed so much, it became completely obsolete! And as recently as DMD 0.166, Oskar's patches for member templates have changed the rules again. Although some 'best practices' are now well understood, some 'how-tos' are still unknown by everyone. Nobody has more than 35 days experience with the current D templates. Not even Walter. <g>
Oct 05 2006
prev sibling next sibling parent Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 04 Oct 2006 15:52:01 -0400, Chad J wrote:

 I'm trying to write a template that, when instantiated, gives a value 
 that is the same as the last time it was instantiated plus one.  
This does not answer your question, but I was wondering why you need this functionality at compile time. I do something similar but it happens at application initialization time and is simple to code. import util.series; import std.stdio; Series UID; // Declare the series. static this() { UID.Next = 1; // Set first value to one. } void main() { writefln( UID.Next ); // should print 1 writefln( UID.Next ); // should print 2 } Where series.d is // ------------------ module util.series; struct Series { private { ulong mValue; ulong mIncr; } ulong Next() { ulong lCV; lCV = mValue; mValue += (mIncr+1); return lCV; } void Next(ulong pInit) { mValue = pInit; } ulong Current() { return mValue; } ulong Prev() { return mValue - (mIncr+1); } void Increment(ulong pInit = 1) { mIncr = pInit-1; } } unittest { Series a; assert( a.Next == 0); assert( a.Next == 1); assert( a.Next == 2); assert( a.Current == 3); a.Increment = 2; assert( a.Next == 3); assert( a.Next == 5); assert( a.Prev == 5); a.Next = 100; assert( a.Current == 100); assert( a.Next == 100); assert( a.Next == 102); } // ------[ end of file ]----------------- -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 5/10/2006 2:15:46 PM
Oct 04 2006
prev sibling parent reply Markus Dangl <danglm in.tum.de> writes:
Chad J > schrieb:
 I'm trying to write a template that, when instantiated, gives a value 
 that is the same as the last time it was instantiated plus one.  Here is 
 some code I tried, but it doesn't work:
 
 import std.stdio;
 
 template GetUID()
 {
     const ulong accumulator;
 
     static if ( ++accumulator != 0 )
         const ulong GetUID = accumulator;
     else
         static assert(0);
 }
 
 void main()
 {
     writefln( GetUID!() ); // should print 1
     writefln( GetUID!() ); // should print 2
 }
 
 Hopefully that gives a good idea of what I am shooting for.
This is a bit complicated to explain: Compile time constructs (templates) work just like a functional language: They are fully deterministic, the only way you can modify the result is by modifying the parameters. If you want to use a counter you'd have to use a recursive approach like this: template counter(uint max, uint i=0) { void initialize() { writefln(i); // Insert your statement hier static if (i<max) counter!(max, i+1).initialize(); } } Although that's perhaps not a real speedup compared to initializing the program with a counter at runtime...
Oct 05 2006
parent reply rm <roel.mathys gmail.com> writes:
Markus Dangl wrote:
 Chad J > schrieb:
 I'm trying to write a template that, when instantiated, gives a value
 that is the same as the last time it was instantiated plus one.  Here
 is some code I tried, but it doesn't work:

 import std.stdio;

 template GetUID()
 {
     const ulong accumulator;

     static if ( ++accumulator != 0 )
         const ulong GetUID = accumulator;
     else
         static assert(0);
 }

 void main()
 {
     writefln( GetUID!() ); // should print 1
     writefln( GetUID!() ); // should print 2
 }

 Hopefully that gives a good idea of what I am shooting for.
This is a bit complicated to explain: Compile time constructs (templates) work just like a functional language: They are fully deterministic, the only way you can modify the result is by modifying the parameters. If you want to use a counter you'd have to use a recursive approach like this: template counter(uint max, uint i=0) { void initialize() { writefln(i); // Insert your statement hier static if (i<max) counter!(max, i+1).initialize(); } } Although that's perhaps not a real speedup compared to initializing the program with a counter at runtime...
could you elaborate with an example I can compile, at the moment I don't see how to put it in a working app thx, roel
Oct 06 2006
parent reply Markus Dangl <danglm in.tum.de> writes:
 Although that's perhaps not a real speedup compared to initializing the
 program with a counter at runtime...
could you elaborate with an example I can compile, at the moment I don't see how to put it in a working app thx, roel
Here is a full example, it compiles and runs with my DMD 0.168: ===== module CompileTimeCounter; import std.stdio; pragma(msg, "-- This is at compile time"); // Converts an integer i to a string at compile time. // Needed for pragma(msg). template itoa(int i) { static if (i < 10) const char[] itoa = "0123456789"[i..i+1]; else const char[] itoa = itoa!(i/10) ~ "0123456789"[i%10]; } // A counter that runs at compile time. template counter(uint max, uint i=0) { void initialize() { pragma(msg, itoa!(i)); writefln(i); static if (i<max) counter!(max, i+1).initialize(); } } // ncounter has to be const, so it is accessible at compile time. const int ncounter = 10; int main(char[][]args) { writefln("-- This is at run time"); counter!(ncounter).initialize(); return 0; } ==== As you can see when you're compiling it counts from 0 to 10 (including 10). When you run the resulting exe file, it will also print the numbers from 0 to 10, but this time there isn't a real counter involved, it's just like having writefln(0); writefln(1); writefln(2); ... in your code. To be exact, each writefln() is wrapped up in a seperate function, this is why i think that a counter at compile time isn't really efficient.
Oct 06 2006
parent reply rm <roel.mathys gmail.com> writes:
Markus Dangl wrote:
 Although that's perhaps not a real speedup compared to initializing the
 program with a counter at runtime...
could you elaborate with an example I can compile, at the moment I don't see how to put it in a working app thx, roel
Here is a full example, it compiles and runs with my DMD 0.168: ===== module CompileTimeCounter; import std.stdio; pragma(msg, "-- This is at compile time"); // Converts an integer i to a string at compile time. // Needed for pragma(msg). template itoa(int i) { static if (i < 10) const char[] itoa = "0123456789"[i..i+1]; else const char[] itoa = itoa!(i/10) ~ "0123456789"[i%10]; } // A counter that runs at compile time. template counter(uint max, uint i=0) { void initialize() { pragma(msg, itoa!(i)); writefln(i); static if (i<max) counter!(max, i+1).initialize(); } } // ncounter has to be const, so it is accessible at compile time. const int ncounter = 10; int main(char[][]args) { writefln("-- This is at run time"); counter!(ncounter).initialize(); return 0; } ==== As you can see when you're compiling it counts from 0 to 10 (including 10). When you run the resulting exe file, it will also print the numbers from 0 to 10, but this time there isn't a real counter involved, it's just like having writefln(0); writefln(1); writefln(2); ... in your code. To be exact, each writefln() is wrapped up in a seperate function, this is why i think that a counter at compile time isn't really efficient.
thx for the itoa stuff, was searching for that! my problem was indeed that I couldn't come up with a identical reusable statement. roel
Oct 06 2006
parent reply Markus Dangl <danglm in.tum.de> writes:
rm schrieb:
 
 thx for the itoa stuff, was searching for that!
 
 my problem was indeed that I couldn't come up with a identical reusable
 statement.
 
 roel
I actually took itoa from Tom S' ctrace: http://www-users.mat.uni.torun.pl/~h3r3tic/ctrace/ I have some compile time lists that i designed myself, and some other stuff, if you ever need lists at compile time contact me by mail :)
Oct 07 2006
parent Don Clugston <dac nospam.com.au> writes:
Markus Dangl wrote:
 rm schrieb:
 thx for the itoa stuff, was searching for that!

 my problem was indeed that I couldn't come up with a identical reusable
 statement.

 roel
I actually took itoa from Tom S' ctrace: http://www-users.mat.uni.torun.pl/~h3r3tic/ctrace/
And he took it from mine... this stuff goes back a long way.
 
 I have some compile time lists that i designed myself, and some other 
 stuff, if you ever need lists at compile time contact me by mail :)
Oct 08 2006