On Wednesday, 17 July 2013 at 23:56:11 UTC, H. S. Teoh wrote:
On Thu, Jul 18, 2013 at 01:02:26AM +0200, JS wrote:
[...]
I can't put together a working example right now(other things
to do)
but the gist of the matter is:
template strsplit(string n) { enum strsplit =
std.string.split(n,
","); }
... inside a ctfe ...
foreach(n; strsplit!(s)) // doesn't work
foreach(n; std.string.split(s, ",")) // works
To me, this is confusing as hell and "n can't be read at
compile
time" took me a good few hours to realize it was simply using
the
template. I'm guessing that it has something to do with the
enum
"return type" of strsplit but I've tried other various things.
I think it will help to understand the difference between
templates and
CTFE. Even though both are processed at compile-time, they are
not the
same thing, and mixing them may not always do what you think
it's doing.
Basically, a template is used for generating code, so any
template
parameters must be known "at compile-time", which means that it
must
ultimately reduce to a typename, or a literal of a built-in
type. Note
that this is NOT the same thing as "this variable is being used
by CTFE
so the compiler should know what its value is at
`compile-time'". At
this conceptual stage of compilation, the compiler hasn't fully
transformed the source code into runnable form yet, so the only
thing it
can do at this point is to work with, conceptually-speaking,
text-level
entities. It can't handle variables and other such things that
only
exist when a program has been transformed into a runnable form
and
executed by an interpreter.
CTFE is basically a subsystem of the compiler that has the
ability to
run code *after* it has been generated (either via parsing the
source
code directly, or via expanding templates). That is to say, the
program
is now in a (semi)runnable form, but the compiler hasn't output
the
object files / executables yet. Basically kinda like an "early
execution" of your program, if you will. Since this happens in a
conceptually later stage than template expansion, whatever's
going on in
CTFE generally can't be fed back into template parameters.
Template
expansion must have been completed before CTFE even comes into
play.
I think part of the confusion may stem from the liberal use of
the term
"compile-time", which may have given a false impression that if
entity X
is known "at compile-time", then it should be usable by
construct Y
which requires stuff that's known "at compile-time". This
oversimplifies
the process of compilation; in reality, "compile-time" consists
of
several distinct conceptual phases (in implementation this may
not
necessarily be true literally, but it helps to understand the
conceptual
stages). What generally happens is that the compiler needs to:
1) Read the text representation of the source code, break that
down
into tokens ("lexing"), and based on the sequence of tokens,
construct
an abstract syntax tree that represents the structure of the
source code
("parsing");
2) Translate the syntax tree into an internal representation
(IR) of the
program that more-or-less maps to the machine code that will
eventually
be output;
3) Translate the IR into actual instructions the target CPU can
understand, and output that as the object file / executable.
Conceptually-speaking, template expansion comes somewhere
between (1)
and (2): it's a mechanism for producing large numbers of
very-similar
subtrees of the syntax tree without requiring the programmer to
do lots
and lots of typing. CTFE is roughly somewhere between (2) and
(3): the
program isn't fully compiled yet, but enough translation has
been done
that it is now in a form that can actually be run (by an
interpreter).
In order to get to this point, template expansion must have been
completed first, since otherwise your syntax tree isn't
complete yet, so
by definition it can't have been translated into a runnable
form yet.
But since template expansion must have already taken place,
that means
you can't feed CTFE values back into templates -- since
otherwise
template expansion couldn't have already taken place!
So, long story short, template parameters must be known at
"text-level",
if we can coin a term for that, the compiler must be able to
reduce said
parameters into a concrete type name or a literal of a built-in
type.
CTFE, on the other hand, works after template expansion has
completed
(conceptually speaking), and so is essentially a kind of "early
execution" of your program. CTFE variables and the like are
"interpreter-level", so they can't be passed back into template
parameters that expect "text-level" input.
The actual details of what the compiler does is, of course,
quite a bit
more complex than the above (over)simplified description, but
generally
speaking, if you stick by the rule that CTFE values can't be
fed back
into the templating system, you will experience much less
frustration.
T
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.
Does this make sense?