Template specialized functions creating runtime instructions?

Jonathan M Davis newsgroup.d at jmdavisprog.com
Wed Aug 21 00:21:43 UTC 2019


On Tuesday, August 20, 2019 5:48:04 PM MDT ads via Digitalmars-d-learn 
wrote:
> This piece of code creates a fizzbuzz string with template
> parameters.
>
> auto fizzbuzz(uint N)() {
>   string accumulate;
>   return fizzbuzz!N(accumulate);
> }
>
> auto fizzbuzz(uint N)(ref string result) if (N % 3 && N % 5) {
>   import std.conv : to;
>
>   result ~= N.to!string ~ "\n";
>   return fizzbuzz!(N - 1)(result);
> }
>
> auto fizzbuzz(uint N)(ref string result) if (!(N % 15)) {
>   result ~= "FizzBuzz\n";
>   return fizzbuzz!(N - 1)(result);
> }
>
> auto fizzbuzz(uint N)(ref string result) if (!(N % 3) && N % 5) {
>   result ~= "Fizz\n";
>   return fizzbuzz!(N - 1)(result);
> }
>
> auto fizzbuzz(uint N)(ref string result) if (!(N % 5) && N % 3) {
>   result ~= "Buzz\n";
>   return fizzbuzz!(N - 1)(result);
> }
>
> auto fizzbuzz(uint N : 0)(ref string result) {
>   return result;
> }
>
> void main() {
>   import std.stdio : writeln;
>
>   fizzbuzz!50().writeln();
> }
>
>
> https://godbolt.org/z/hWENgc
>
> In the generated assembly, it looks like it is creating a lot of
> runtime instructions, contrary to my belief that templated codes
> are purely compile-time. I was expecting that the compiler would
> deduce the fizzbuzz string until 50 in compile-time and just
> print it in the run-time. Why is this not the case?

Function templates create normal functions when they're instantiated. They
aren't going to be removed from the binary any more than a non-templated
function would be. And the compiler has no way of knowing which functions
can be removed from the final executable. It's just creating object files
that get linked into the final executable. It's the linker that would have
to strip unnecessary stuff out. And since fizzbuzz!50 is called at runtime,
even if the linker did strip out functions that weren't needed, it couldn't
strip out any of fizzbuzz!50 or anything that it calls.

The compiler doesn't call functions at compile time unless they're used in a
context where the value must be known at compile time. So,

fizzbuzz!50.writeln();

is not going to involve any fnuctions being called at compile time. Each
function template that needs to be instantiated would be instantiated, but
that just creates a bunch of functions that can be called. For any functions
to be called at compile time, you'd have to force it by calling a function
in a context where the result must be known at compile time. e.g.

enum value = fizzbuzz!50();

would result in fizzbuzz!50 being called at compile time, and then the value
could be passed to writeln at runtime.

If you actually want templates to do all of their work at compile time, then
you wouldn't use functions. You'd just use eponymous templates. e.g.
something like

template factorial(int n)
{
    static if(n == 1)
        enum factorial = n;
    else
        enum factorial = factorial!(n - 1) * n;
}

in which case the you'd use it by doing something like

enum result = factorial!5;

or

int result = factorial!5;

In either case, because factorial is purely a template, the work would be
done at compile time.

Alternatively, you can just call functions at compile time - you just have
to make sure that the call is in a context where the result must be known at
compile time. e.g.

int factorial(int n)
{
    int result = 1;
    foreach(i; 2 .. n + 1)
        result *= i;
    return result;
}

enum result = factorial(5);

There arguably isn't much point in using templated functions the way you
are. It would be simpler to just write a function that calculated the result
normally, and then if you want to have the result at compile time, you just
call the function and use the result to give an enum its value.

- Jonathan M Davis





More information about the Digitalmars-d-learn mailing list