foreach over split string

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Jul 17 22:56:12 PDT 2013


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

-- 
Winners never quit, quitters never win. But those who never quit AND never win are idiots.


More information about the Digitalmars-d-learn mailing list