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 entornos de ejecución de terceros permiten su uso en Mac OS X 10.5 e iOS 2.2+ [2] y sistemas que no sean de Apple.
Apple diseñó bloques con el objetivo explícito de facilitar la escritura de programas para la arquitectura de subprocesamiento Grand Central Dispatch , [3] [4] aunque es independiente de esa arquitectura y se puede utilizar de la misma manera que los cierres en otros lenguajes. Apple ha implementado bloques tanto en su propia rama de la Colección de compiladores GNU [1] como en el frontend del compilador Clang LLVM . El soporte de la biblioteca de ejecución de lenguaje 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 de C ordinarias, su valor puede capturar el estado de su contexto circundante. Una definición de bloque produce un valor opaco que contiene tanto una referencia al código dentro del bloque como una instantánea del estado actual de las variables de la pila local en el momento de su definición. El bloque puede invocarse posteriormente de la misma manera que un puntero de función. El bloque puede asignarse a variables, pasarse a funciones y tratarse de otro modo 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 ámbito en el que se definió.
Dado un valor de bloque, el código dentro del bloque se puede ejecutar en cualquier momento posterior llamándolo, utilizando la misma sintaxis que se usaría para llamar a una función.
Un ejemplo simple que captura el 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 que devuelva 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 mycounter = MakeCounter ( 5,2 ) ; printf ( "Primera llamada: %d \n " , mycounter ()); printf ( " Segunda llamada: %d \n " , mycounter ()); printf ( "Tercera llamada: %d \n " , mycounter ()); /* debido a que fue copiado, también debe ser liberado */ Block_release ( mycounter ); return 0 ; }
$ clang -fblocks blocks-test.c # Mac OS X $ ./a.out Primera llamada: 5 Segunda llamada: 7 Tercera llamada: 9
El entorno de ejecución de bloques no forma parte de las bibliotecas C vinculadas de forma predeterminada en algunos sistemas. Si este es el caso, es necesario vincularlo explícitamente a esta biblioteca:
$ clang -fblocks bloques-test.c -lBlocksRuntime # Linux
El entorno de ejecución es parte del entorno de ejecución de clang, pero a veces no se instala con el paquete clang. Hay disponible un entorno de ejecución independiente extraído de compiler-rt. [7]
Los bloques tienen un parecido superficial con la extensión de C de GCC para soportar funciones anidadas con alcance 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 thunks 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 thunks 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 thunks ejecutables, por lo que no comparten esta debilidad. Por otro lado, los bloques introducen un tipo completamente nuevo para el puntero, mientras que los punteros a funciones anidadas en GCC son punteros de función regulares y se pueden usar directamente con el código existente.