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