www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - static foreach

reply Navin Kumar <navinkumar+dlang gmail.com> writes:
I was going over the D tutorials on tuples and noticed that it's possible to do
a "foreach" across the tuple items.  I'm assuming this is a compile-time
foreach, is that correct?

I've been looking for a way to do a compile-time "foreach" for a different
problem, namely complete loop unrolling.  I have a performance-sensitive "atoi"
routine that knows, at compile-time, the length of the string (but not the
string itself).  I've been thinking of solving this problem using static if(len
== 1) { .. } static if(len == 2) { .. } and so on up to len = 22, and not
supporting any longer than that.  It severely violates the "DRY" principle, and
the function becomes a thousand lines of code (and a nightmare to ever change,
since changes have to be made 22x times).

I would like to solve this problem with a compile-time foreach; how do I do so?
 "static foreach" is not valid syntax in D.
Mar 28 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Navin Kumar:

 I was going over the D tutorials on tuples and noticed that it's possible to
do a "foreach" across the tuple items.  I'm assuming this is a compile-time
foreach, is that correct?

Correct.
 I would like to solve this problem with a compile-time foreach; how do I do
so?  "static foreach" is not valid syntax in D.

In my dlibs1 I have: template Range(int stop) { static if (stop <= 0) alias Tuple!() Range; else alias Tuple!(Range!(stop-1), stop-1) Range; } /// ditto template Range(int start, int stop) { static if (stop <= start) alias Tuple!() Range; else alias Tuple!(Range!(start, stop-1), stop-1) Range; } /// ditto template Range(int start, int stop, int step) { static assert(step != 0, "Range: step must be != 0"); static if (step > 0) { static if (stop <= start) alias Tuple!() Range; else alias Tuple!(Range!(start, stop-step, step), stop-step) Range; } else { static if (stop >= start) alias Tuple!() Range; else alias Tuple!(Range!(start, stop-step, step), stop-step) Range; } } // End Range!(a,b,c) With that you can unroll loops and push multiple cases inside a switch statement. With a little more work you can unroll in cases where you don't know the number of loops, using a modulus and vision approach. The main problem, baside a bit of bloat at compile time, is that such forach(i; Range!(...)) doesn't work outside functions, as a good static foreach has to. Bye, bearophile
Mar 28 2010
next sibling parent reply Navin Kumar <navinkumar+dlang gmail.com> writes:
Thanks.  It would still be nice to see full "static foreach" functionality so
that, as you pointed out, it can be used outside of functions, to add member
variables to a struct, for instance.   I know people have been using m4 with C
to achieve compile-time loops and branches.  D's "static if" is vastly superior
to m4 because it has access to typeinfo and routines written in D.

I'm a little confused, though, why the author chose to overload "foreach" to
either be a runtime loop or a compile-time loop based on whether its looping
over a tuple or not.  It seems it would be more readable to have "static
foreach" and simply put a caveat that it's only supported inside functions for
now.
Mar 28 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Navin Kumar:
It seems it would be more readable to have "static foreach" and simply put a
caveat that it's only supported inside functions for now.<

I agree. I have recently written about it: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=107873 If you see at this document (about half of those ideas are now present in D2) http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf At page 52 you can see that Walter&Andrei too were appreciating the idea of a static foreach. So I presume you will see it in D3 or even sooner :-) I presume it's not a hard thing to implement. But Walter is busy, and Andrei's book was already late. Bye, bearophile
Mar 28 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
so:
 Reading it again, how i missed page 34, awesome!

Page 6 is about The Rule of Least Astonishment, D tries hard (and often succeeds) to be uniform, to do the same things when you use the same syntax in different parts of the language. But I have hitted a case, "T : T*" can mean different things when used in a template or in a is(): template IsPointer1(T : T*) { enum bool IsPointer1 = true; } template IsPointer1(T) { enum bool IsPointer1 = false; } template IsPointer2(T) { enum bool IsPointer2 = is(T : T*); } void main() { static assert(IsPointer1!(int*)); static assert(IsPointer2!(int*)); // asserts } This is a dis-uniformity. I have found it only years of using D1, and it has caused me few troubles. Bye, bearophile
Mar 28 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/28/2010 05:09 PM, bearophile wrote:
 Navin Kumar:
 It seems it would be more readable to have "static foreach" and simply put a
caveat that it's only supported inside functions for now.<

I agree. I have recently written about it: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=107873 If you see at this document (about half of those ideas are now present in D2) http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf At page 52 you can see that Walter&Andrei too were appreciating the idea of a static foreach. So I presume you will see it in D3 or even sooner :-) I presume it's not a hard thing to implement. But Walter is busy, and Andrei's book was already late. Bye, bearophile

static foreach was part of the plan until recently, but Walter encountered severe implementation difficulties so we had to abandon it. I agree that it's odd that the same statement iterates at compile-time or run-time depending on the iterated type. Andrei
Mar 28 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:
 static foreach was part of the plan until recently, but Walter 
 encountered severe implementation difficulties so we had to abandon it.

I didn't know this. It looks like a simple thing to implement, but it must be an illusion.
 I agree that it's odd that the same statement iterates at compile-time 
 or run-time depending on the iterated type.

Between implementing a good static foreach that can be used outside functions too, and the current situation, there is an intermediate possibility: to require the prefix keyword "static" before "foreach" when the foreach is done on a tuple, that is when it's static. This changes nothing in the current D2 semantics. But it makes code a little more readable because you can tell apart with no problems the static case of loop on a tuple from the other one. [Later if Walter finds a way to implement, the static foreach can be made richer, for example letting the compiler translate "static foreach (i; 10 .. 20)" in a "static foreach (i; Range!(10, 20))". And even later if Walter is able the whole thing can be allowed outside functions.] Bye, bearophile
Mar 28 2010
prev sibling parent so <so so.do> writes:
 If you see at this document (about half of those ideas are now present  
 in D2)
 http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf

Reading it again, how i missed page 34, awesome! -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Mar 28 2010