Los bloques son una extensión no estándar agregada por Apple Inc. a las implementaciones de Clang de los lenguajes de programación C , C++ y Objective-C que utiliza una sintaxis similar a una expresión lambda para crear cierres dentro de estos lenguajes. Los bloques son compatibles con programas desarrollados para Mac OS X 10.6+ y iOS 4.0+, [1] aunque los tiempos de ejecución de terceros permiten su uso en Mac OS X 10.5 y iOS 2.2+ [2] y sistemas que no sean Apple.
Apple diseñó bloques con el objetivo explícito de facilitar la escritura de programas para la arquitectura de subprocesos de Grand Central Dispatch , [3] [4] aunque es independiente de esa arquitectura y puede usarse de manera muy similar a los cierres en otros lenguajes. Apple ha implementado bloques tanto en su propia rama de GNU Compiler Collection [1] como en la interfaz del compilador Clang LLVM . La compatibilidad con la biblioteca de lenguaje de ejecución para bloques también está disponible como parte del proyecto LLVM. El grupo Khronos utiliza la sintaxis de bloques para poner en cola los núcleos desde dentro de los núcleos a partir de la versión 2.0 de OpenCL . [5]
Al igual que las definiciones de funciones, los bloques pueden tomar argumentos y declarar sus propias variables internamente. A diferencia de las definiciones de funciones C ordinarias, su valor puede capturar el estado del contexto circundante. Una definición de bloque produce un valor opaco que contiene una referencia al código dentro del bloque y una instantánea del estado actual de las variables de la pila local en el momento de su definición. Posteriormente, el bloque se puede invocar de la misma manera que un puntero de función. El bloque puede asignarse a variables, pasarse a funciones y, de lo contrario, tratarse como un puntero de función normal, aunque el programador de la aplicación (o la API) debe marcar el bloque con un operador especial (Block_copy) si se va a utilizar fuera del alcance en que fue definido.
Dado un valor de bloque, el código dentro del bloque se puede ejecutar en cualquier momento posterior llamándolo, usando la misma sintaxis que se usaría para llamar a una función.
Un ejemplo simple que captura un estado mutable en el ámbito circundante es un iterador de rango entero : [6]
/* blocks-test.c */ #include <stdio.h> #include <Block.h> /* Tipo de bloque que no toma nada y devuelve un int */ typedef int ( ^ IntBlock )(); IntBlock MakeCounter ( int inicio , int incremento ) { __block int i = inicio ; return Block_copy ( ^ ( void ) { int ret = i ; i += incremento ; return ret ; }); } int main ( void ) { IntBlock micontador = MakeCounter ( 5 , 2 ); printf ( "Primera llamada: %d \n " , micontador ()); printf ( "Segunda llamada: %d \n " , micontador ()); printf ( "Tercera llamada: %d \n " , micontador ()); /* debido a que fue copiado, también debe liberarse */ Block_release ( mycounter ); devolver 0 ; }
$ clang -fblocks blocks-test.c # Mac OS X $ ./a.out Primera llamada: 5 Segunda llamada: 7 Tercera llamada: 9
El tiempo de ejecución de los bloques no forma parte de las bibliotecas C vinculadas de forma predeterminada en algunos sistemas. Si este es el caso, es necesario vincular explícitamente a esta biblioteca:
$ clang -fblocks bloques-test.c -lBlocksRuntime # Linux
El tiempo de ejecución es parte del tiempo de ejecución de clang, pero a veces no se instala con el paquete clang. Está disponible un tiempo de ejecución independiente extraído de compilador-rt. [7]
Los bloques tienen un parecido superficial con la extensión de C de GCC para soportar funciones anidadas de ámbito léxico . [8] Sin embargo, las funciones anidadas de GCC, a diferencia de los bloques, no deben llamarse después de que el alcance que las contiene haya salido, ya que eso daría como resultado un comportamiento indefinido .
Las funciones anidadas de estilo GCC actualmente utilizan la creación dinámica de procesadores ejecutables en la mayoría de las arquitecturas al tomar la dirección de la función anidada. En la mayoría de las arquitecturas (incluida X86), estos procesadores se crean en la pila, lo que requiere marcar la pila como ejecutable. Las pilas ejecutables generalmente se consideran un potencial agujero de seguridad. Los bloques no requieren el uso de procesadores ejecutables, por lo que no comparten esta debilidad. Por otro lado, los bloques introducen un tipo completamente nuevo de puntero, mientras que los punteros a funciones anidadas en GCC son punteros de funciones normales y se pueden usar directamente con el código existente.