On Thursday, 18 July 2013 at 05:57:48 UTC, H. S. Teoh wrote:
On Thu, Jul 18, 2013 at 07:23:57AM +0200, JS wrote:
[...]
Thanks, this has made it much clearer.

Something like

foreach(a; StrSplit!(s))
   foreach(b; StrSplit !(a))

does work because the second StrSplit uses a "ctfe-time variable"
instead of a "template-time variable".

My logic was:

1. first StrSplit resolved
2. first foreach evaluated
3. second StrSplit resolved
4. second foreach evaluated

while it actually is

1. first StrSplit resolved
2. second StrSplit resolved
3. foreach's resolved

because template expansion happens before any ctfe expansion.

I guess I was thinking the compiler would be smart enough to
interleave template expansion and ctfe code(which would be much more
powerfull).

Effectively template expansion is a sort of pre-processing to ctfe
code and must be static as far as ctfe's go.
[...]

That's one way to think of it, yes.

As for interleaving template expansion vs. ctfe evaluation, the compiler *does* do that to some extent. For example, this code does work:

        // Function that can be evaluated by CTFE
        int func(int x) pure {
                int sum;
                foreach (i; 0..x) {
                        sum += i;
                }
                return sum;
        }

        // Force CTFE evaluation
        enum myConst = func(10);

        // Template that requires an int parameter.
        template MyTemplate(int x) {
                enum MyTemplate = x+10;
        }

        // Instantiate template with enum produced by CTFE.
        pragma(msg, MyTemplate!myConst);

        void main() {}

The reason this works is because the compiler is smart enough to figure out that myConst requires func, so it first compiles func far enough to be CTFE-evaluable, then it evaluates func to produce the value of myConst, and then myConst is used to instantiate MyTemplate. You could think of it as the compiler compiling different parts of the program at different rates, so func has been compiled into a runnable state, but
the pragma(msg) line is still at the template expansion state.

Note, however, that the template-before-CTFE limitation still applies: func can't require template expansion while it's running; it must be entirely compilable into runnable state before CTFE can evaluate it. Other parts of the program can still remain at the template-expansion
stage, so they can take some CTFE-produced values as template
parameters. But you can't make any reverse dependencies / loops. You're OK if you already have runnable code that can then produce template parameters for other code, but you can't run CTFE and template expansion
simultaneously in the *same* code.

So the compiler is actually pretty smart about reordering these things, but the fundamental limitation of template-before-CTFE still applies to each individual code unit. After all, you can't run code that hasn't
been fully expanded by the template system yet.


T

Thanks. Your reply was extremely helpful and I think I'll have less pms over ctfe's.


Reply via email to