En informática , la ejecución de funciones en tiempo de compilación (o evaluación de funciones en tiempo de compilación , o expresiones constantes generales ) es la capacidad de un compilador , que normalmente compilaría una función a código máquina y la ejecutaría en tiempo de ejecución , para ejecutar la función en tiempo de compilación . Esto es posible si los argumentos de la función se conocen en tiempo de compilación y la función no hace referencia ni intenta modificar ningún estado global (es decir, es una función pura ).
Si solo se conoce el valor de algunos de los argumentos, el compilador aún puede realizar cierto nivel de ejecución de funciones en tiempo de compilación ( evaluación parcial ), lo que posiblemente produzca un código más optimizado que si no se conociera ningún argumento.
Ejemplos
Ceceo
El sistema de macros de Lisp es un ejemplo temprano del uso de la evaluación en tiempo de compilación de funciones definidas por el usuario en el mismo lenguaje.
C++
La extensión Metacode para C++ (Vandevoorde 2003) [ 1 ] fue un sistema experimental inicial para permitir la evaluación de funciones en tiempo de compilación (CTFE) y la inyección de código como una sintaxis mejorada para la metaprogramación de plantillas de C++ .
En versiones anteriores de C++ , la metaprogramación de plantillas se usa a menudo para calcular valores en tiempo de compilación, como el siguiente. En C y C++, los enums se usaban históricamente para crear constantes enteras con nombre en tiempo de compilación (sin macros de preprocesador ), antes de la adición de constexpr en C++11 .
plantilla < entero sin signo N > estructura Factorial { enumeración { VALOR = N * Factorial < N - 1 >:: VALOR }; };plantilla <> struct Factorial < 0 > { enum { VALOR = 1 }; };void foo () { int x = Factorial < 0 >:: VALUE ; // == 1 int y = Factorial < 4 >:: VALUE ; // == 24 }Utilizando la evaluación de funciones en tiempo de compilación, el código utilizado para calcular el factorial sería similar al que se escribiría para la evaluación en tiempo de ejecución, por ejemplo, utilizando constexpr.
importar std ;constexpr unsigned int factorial ( unsigned int n ) noexcept { return n ? ( n * factorial ( n - 1 )) : 1 ; }constexpr unsigned int f10 = factorial ( 10 );int main () { std :: println ( "{}" , f10 ); return 0 ; }En C++11, esta técnica se conoce como expresiones constantes generalizadas ( constexpr). [ 2 ] C++14 relaja las restricciones de constexpr, permitiendo declaraciones locales y el uso de condicionales y bucles (la restricción general de que todos los datos necesarios para la ejecución estén disponibles en tiempo de compilación permanece).
// Factorial iterativo en tiempo de compilación. constexpr unsigned int factorial ( unsigned int n ) noexcept { unsigned int result = 1 ; while ( n > 1 ) { result *= n -- ; } return result ; }int main () { constexpr int f4 = factorial ( 4 ); // f4 == 24 }En C++20 , se introdujeron las funciones inmediatas con la palabra clave `consteval` , y la ejecución de funciones en tiempo de compilación se hizo más accesible y flexible con restricciones menos restrictivas.constexpr
// Factorial iterativo en tiempo de compilación. consteval unsigned int factorial ( unsigned int n ) noexcept { unsigned int result = 1 ; while ( n > 1 ) { result *= n -- ; } return result ; }int main () { int f4 = factorial ( 4 ); // f4 == 24 }Dado que la función factorialestá marcada como consteval, se garantiza su invocación en tiempo de compilación sin ser forzada en otro contexto de evaluación manifiestamente constante. Por lo tanto, el uso de funciones inmediatas ofrece amplias aplicaciones en metaprogramación y verificación en tiempo de compilación. Por ejemplo, esto se utiliza en std::format(). [ 3 ]
void assertionFailed () {}consteval void testAssert ( bool cond ) { if ( ! cond ) { assertionFailed (); } }consteval void test () { int x = 10 ; testAssert ( x == 10 ); // ok x ++ ; testAssert ( x == 11 ); // ok x -- ; testAssert ( x == 12 ); // falla aquí }int main () { prueba (); }En este ejemplo, la compilación falla porque la función inmediata invoca una función que no se puede usar en expresiones constantes. En otras palabras, la compilación se detiene tras una aserción fallida.
El mensaje de error de compilación típico sería el siguiente:
En la función ' int main () ' : en la expansión ' constexpr ' de ' test () ' en la expansión ' constexpr ' de ' testAssert ( x == 12 ) ' error : llamada a la función no ' constexpr ' ' assertionFailed ( ) ' assertionFailed (); ~~~~~~~~~~~~~~~^~ [ ... ]Este es un ejemplo del uso de funciones inmediatas como constructores, para la comprobación de argumentos en tiempo de compilación:
importar std ;usando std :: string_view ;void assertionFailed () {}struct CheckedMessage { string_view msg ;consteval CheckedMessage ( const char msg []) : msg { msg } { if ( msg . ends_with ( '!' )) { assertionFailed (); } } };void sendCalmMessage ( const CheckedMessage & arg ) { std :: println ( "{}" , arg . msg ); }int main () { sendCalmMessage ( "Hola, mundo" ); sendCalmMessage ( "¡Hola, mundo!" ); }La compilación falla aquí con el siguiente mensaje:
En la función ' int main () ' : en la expansión ' constexpr ' de ' CheckedMessage ( "¡Hola, mundo!" ) ' error : llamada a una función que no es ' constexpr ' ' void assertionFailed () ' assertionFailed (); ~~~~~~~~~~~~~~~^~ [ ... ]D
Este es un ejemplo de evaluación de funciones en tiempo de compilación en el lenguaje de programación D : [ 4 ]
uint factorial ( uint n ) { if ( n == 0 ) { return 1 ; } return n * factorial ( n - 1 ); }// calculado en tiempo de compilación enum y = factorial ( 0 ); // == 1 enum x = factorial ( 4 ); // == 24Este ejemplo especifica una función D válida llamada "factorial" que normalmente se evaluaría en tiempo de ejecución. El uso de enumle indica al compilador que el inicializador para las variables debe calcularse en tiempo de compilación. Nótese que los argumentos de la función también deben poder resolverse en tiempo de compilación. [ 5 ]
CTFE se puede utilizar para rellenar estructuras de datos en tiempo de compilación de forma sencilla (versión D 2):
uint [] genFactorials ( uint n ) { uint [] result = new uint [ n ]; result [ 0 ] = 1 ; foreach ( i ; 1 .. n ) { result [ i ] = result [ i - 1 ] * i ; } return result ; }enum factoriales = genFactoriales ( 13 );void main () {}// 'factorials' contiene en tiempo de compilación: // [1, 1, 2, 6, 24, 120, 720, 5_040, 40_320, 362_880, 3_628_800, // 39_916_800, 479_001_600]CTFE se puede utilizar para generar cadenas que luego se analizan y compilan como código D en D.
Óxido
Este es un ejemplo de evaluación de funciones en tiempo de compilación en Rust . Esto se hace usando . [ 6 ]constfn
const fn add ( a : i32 , b : i32 ) -> i32 { a + b }const RESULTADO : i32 = agregar ( 10 , 5 );fn main () { // El valor 15 ya está incorporado en el binario aquí println! ( "El resultado es: {}" , RESULT ); }Zig
Este es un ejemplo de evaluación de funciones en tiempo de compilación en Zig . [ 7 ]
pub fn factorial ( n : usize ) usize { var result = 1 ; for ( 1 ..( n + 1 )) | i | { result *= i ; } return result ; }pub fn main () void { const x = comptime factorial ( 0 ); // == 0 const y = comptime factorial ( 4 ); // == 24 }Este ejemplo especifica una función válida de Zig llamada "factorial", que normalmente se evaluaría en tiempo de ejecución. El uso de comptimele indica al compilador que el inicializador de las variables debe calcularse en tiempo de compilación. Tenga en cuenta que los argumentos de la función también deben poder resolverse en tiempo de compilación.
Zig también admite parámetros en tiempo de compilación. [ 8 ]
pub fn factorial ( comptime n : usize ) usize { var result : usize = 1 ; for ( 1 ..( n + 1 )) | i | { result *= i ; } return result ; }pub fn main () void { const x = factorial ( 0 ); // == 0 const y = factorial ( 4 ); // == 24 }CTFE se puede utilizar para crear estructuras de datos genéricas en tiempo de compilación:
fn List ( comptime T : type ) type { return struct { items : [] T , len : usize , }; }// La estructura de datos genérica List se puede instanciar pasando un tipo: var buffer : [ 10 ] i32 = undefined ; var list = List ( i32 ){ . items = & buffer , . len = 0 , };Referencias
- ^ Daveed Vandevoorde, Edison Design Group (18 de abril de 2003). "Metaprogramación reflexiva en C++" (PDF) . Consultado el 19 de julio de 2015 .
- ↑ Gabriel Dos Reis y Bjarne Stroustrup (marzo de 2010). "Expresiones constantes generales para lenguajes de programación de sistemas. SAC-2010. El 25.º Simposio ACM sobre Computación Aplicada" (PDF) .
- ↑ cppreference.com (10 de mayo de 2026). "std::format" . cppreference.com . cppreference.com.
- ↑ Especificación del lenguaje D 2.0: Funciones
- ↑ Especificación del lenguaje D 2.0: Atributos
- ↑ El equipo de Rust (16 de abril de 2026). "Evaluación constante" . docs.rust-lang.org . El equipo de Rust.
- ↑ Referencia del lenguaje Zig 0.11.0: Expresiones en tiempo de compilación
- ↑ Referencia del lenguaje Zig 0.11.0: Parámetros de tiempo de compilación
Enlaces externos
- Construcción de compiladores
- Optimizaciones del compilador