The Lisp macro system is an early example of the use of compile-time evaluation of user-defined functions in the same language.
The Metacode extension to C++ (Vandevoorde 2003)1 was an early experimental system to allow compile-time function evaluation (CTFE) and code injection as an improved syntax for C++ template metaprogramming.
In earlier versions of C++, template metaprogramming is often used to compute values at compile time, such as:
Using compile-time function evaluation, code used to compute the factorial would be similar to what one would write for run-time evaluation e.g. using C++11 constexpr.
In C++11, this technique is known as generalized constant expressions (constexpr).2 C++14 relaxes the constraints on constexpr – allowing local declarations and use of conditionals and loops (the general restriction that all data required for the execution be available at compile-time remains).
Here's an example of compile-time function evaluation in C++14:
In C++20, immediate functions were introduced, and compile-time function execution was made more accessible and flexible with relaxed constexpr restrictions.
Since function Factorial is marked consteval, it is guaranteed to invoke at compile-time without being forced in another manifestly constant-evaluated context. Hence, the usage of immediate functions offers wide uses in metaprogramming, and compile-time checking (used in C++20 text formatting library).
Here's an example of using immediate functions in compile-time function execution:
In this example, the compilation fails because the immediate function invoked function which is not usable in constant expressions. In other words, the compilation stops after failed assertion.
The typical compilation error message would display:
Here's another example of using immediate functions as constructors which enables compile-time argument checking:
The compilation fails here with the message:
Here's an example of compile-time function evaluation in the D programming language:3
This example specifies a valid D function called "factorial" which would typically be evaluated at run time. The use of enum tells the compiler that the initializer for the variables must be computed at compile time. Note that the arguments to the function must be able to be resolved at compile time as well.4
CTFE can be used to populate data structures at compile-time in a simple way (D version 2):
CTFE can be used to generate strings which are then parsed and compiled as D code in D.
Here's an example of compile-time function evaluation in the Zig programming language:5
This example specifies a valid Zig function called "factorial" which would typically be evaluated at run time. The use of comptime tells the compiler that the initializer for the variables must be computed at compile time. Note that the arguments to the function must be able to be resolved at compile time as well.
Zig also support Compile-Time Parameters.6
CTFE can be used to create generic data structures at compile-time:
Daveed Vandevoorde, Edison Design Group (April 18, 2003). "Reflective Metaprogramming in C++" (PDF). Retrieved July 19, 2015. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1471.pdf ↩
Gabriel Dos Reis and Bjarne Stroustrup (March 2010). "General Constant Expressions for System Programming Languages. SAC-2010. The 25th ACM Symposium On Applied Computing" (PDF). http://www.stroustrup.com/sac10-constexpr.pdf ↩
D 2.0 language specification: Functions http://d-programming-language.org/function.html#interpretation ↩
D 2.0 language specification: Attributes http://d-programming-language.org/attribute.html#const ↩
Zig 0.11.0 Language Reference: Compile-Time Expressions https://ziglang.org/documentation/0.11.0/#Compile-Time-Expressions ↩
Zig 0.11.0 Language Reference: Compile-Time Parameters https://ziglang.org/documentation/0.11.0/#Compile-Time-Parameters ↩