CTFE calling a template: Error: expression ... is not a valid template value argument

Jonathan M Davis jmdavisProg at gmx.com
Thu Sep 20 15:29:48 PDT 2012


On Friday, September 21, 2012 00:11:51 Jens Mueller wrote:
> I thought foo is interpreted at compile time.
> There seems to be a subtle difference I'm not getting.
> Because you can do the factorial using CTFE even though you have
> recursion. I.e. there you have a call to the function itself. I.e. it
> can be compiled because you just insert a call to the function. But for
> a template you cannot issue something like call for instantiation.
> Have to think more about it. But your answer helps a lot. Pushes me in
> the right direction.

Okay. Straight up recursion works. So, with this code

int func(int value)
{
 if(value < 10)
 return func(value + 1);
 return value;
}

enum var = func(5);

var would be 10. The problem is that you're trying to pass the result of a 
recursive call as a template argument. As far as a function's behavior goes, 
it's identical regardless of whether it's run at compile time or runtime (save 
that __ctfe is true at compile time but not runtime). To quote the docs:

------
Any func­tions that ex­e­cute at com­pile time must also be ex­e­cutable at 
run time. The com­pile time eval­u­a­tion of a func­tion does the equiv­a­lent 
of run­ning the func­tion at run time. This means that the se­man­tics of a 
func­tion can­not de­pend on com­pile time val­ues of the func­tion. For ex­
am­ple:

int foo(char[] s) {
 return mixin(s);
}

const int x = foo("1");

is il­le­gal, be­cause the run­time code for foo() can­not be gen­er­ated. A 
func­tion tem­plate would be the ap­pro­pri­ate method to im­ple­ment this 
sort of thing.
------

You're doing something very similar to passing a function argument to a mixin 
statement, but in this case, it's passing the result of calling a function 
which doesn't exist yet (since it hasn't been fully compiled) to a template.

In order for your foo function to be called, it must be fully compiled first 
(including its entire body, since CTFE needs the full definition of the 
function, not just its signature). The body cannot be fully compiled until the 
template that it's using is instantiated. But that template can't be compiled 
until foo has been compiled, because you're passing a call to foo to it as a 
template argument. So, you have a circular dependency.

Normal recursion avoids this, because it only depends on the function's 
signature, but what you're doing requires that the function be _run_ as part 
of the process of defining it. That's an unbreakable circular dependency and 
will never work. You need to redesign your code so that you don't require a 
function to call itself while it's being defined. Being called at compile time 
is fine, but being called while it's being compiled is not.

- Jonathan M Davis


More information about the Digitalmars-d mailing list