Cedro atlántico azul Use el #embed de C23 hoy con el pre-procesador Cedro Español, English Sentido-Labs.com

2022-08-04

El C23 está a la vuelta de la esquina y ya sabemos qué características va a añadir al lenguage C.

Una de ellas es la nueva directiva de pre-procesador #embed, que permite incluir ficheros binarios en forma de ristra de octetos.

#include <stdint.h> const uint8_t imagen[] = { #embed "../images/cedro-32x32.png" }; #include <stdint.h> const uint8_t imagen[] = { 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,… 0x00,0x00,0x00,0x20,0x00,0x00,0x00,… ⋮ };

El compilador no necesita realmente generar ese código fuente, sólo funcionar «como si» lo hubiera hecho. De esta forma es libre de insertarlo directamente al enlazar los ficheros objeto al final, sin pasarlo por las otras fases de la compilación.

Mientras esperamos a que nuestro compilador favorito incorpore esta prestación, podemos empezar a usarla immediatamente con el pre-procesador Cedro.

La única diferencia en el código fuente es una «pragma» especial que indica a Cedro que se ocupe del #embed en vez de pasárselo al compilador.

#include <stdint.h> #pragma Cedro 1.0 #embed const uint8_t imagen[] = { #embed "../images/cedro-32x32.png" };
#include <stdint.h> const uint8_t imagen[] = { /* cedro-32x32.png */ 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,… 0x00,0x00,0x00,0x20,0x00,0x00,0x00,… ⋮ };

Cuando cambiemos a un compilador que implemente #embed, sólo tendremos que eliminar la línea #pragma Cedro 1.0 #embed.

Cedro puede usarse de dos maneras:

Insertar como cadena #embed-as-string

¿No es ineficiente el generar todos esos literales de octetos y compilarlos? Sí que lo es. Por eso ha sido necesario añadir #embed a la norma C23, y Cedro puede usar otra técnica que es más eficiente, aunque depende del compilador C el que funcione con un fichero binario concreto: la opción --embed-as-string=1234567 por ejemplo indica a Cedro que use literales de cadena si el fichero binario es menor de 1234567 octetos. Si el fichero es mayor, se insertará con literales de octetos que es menos eficiente, pero más compatible.

#pragma Cedro 1.0 #embed const char const sombreador_de_fragmento[] = { #embed "shader.frag.glsl" , 0x00 // Cadena terminada con zero. }; --embed-as-string=170 const char const sombreador_de_fragmento[164] = /* shader.frag.glsl */ "#version 140\n" "\n" "precision highp float; // needed only for version 1.30\n" "\n" "in vec3 ex_Color;\n" "out vec4 out_Color;\n" "\n" "void main(void)\n" "{\n" "\tout_Color = vec4(ex_Color,1.0);\n" "}\n";

Nótese cómo Cedro se da cuenta de que el último octeto es cero y lo elimina, porque al haber una cadena justo antes, el compilador añadirá el terminador cero automáticamente.

En el siguiente ejemplo, al no haber un octeto cero tras la línea #embed, el tamaño 1559 corresponde exactamente al tamaño del fichero. Así evitamos tener un cero de más al final por usar cadenas.

#include <stdint.h> #pragma Cedro 1.0 #embed const uint8_t imagen[] = { #embed "../images/cedro-32x32.png" }; --embed-as-string=1600 #include <stdint.h> const uint8_t imagen[1559] = /* cedro-32x32.png */ "\211PNG\r\n" "\032\n" "\000\000\000\rIHDR\000\000\000 \000\000\000 \b\002…" ⋮ …";

La diferencia puede ser notable con ficheros grandes. Por ejemplo, estas medidas corresponden a un fichero de 8 MB, compilado con una CPU IBM Power9 en Linux, todos los ficheros en disco RAM:

Generar códigoCompilar con GCC 11Compilar con clang 12
cedro0.23 s1.56 MB27.66 s1328.13 MB30.44 s984.59 MB
cedro --embed-as-string=…0.17 s1.87 MB0.99 s125.32 MB0.42 s144.95 MB

Hay otras maneras de incorporar ficheros a programas en C. La ventaja de usar Cedro es que el código fuente es el mismo y así es muy fácil usar compiladores diferentes, sólo añadiendo o eliminando la línea #pragma.

Cedro proporciona otras prestaciones que tal vez le interesen:

  1. La macro pespunte @backstitch» en inglés].
  2. Devolución diferida de recursos auto ... o defer ....
  3. Salida de bucles anidados break etiqueta;.
  4. Notación para porciones de ristras ristra[inicio..fin].
  5. Macros de bloque #define { ... #define }.
  6. Macros de bucle #foreach { ... #foreach }.
  7. Inclusión binaria #include {...}/#embed "...".
  8. Mejores literales numéricos (12'34 | 12_341234, 0b10100xA).

Cedro puede descargarse, bajo la licencia Apache 2.0, en su página web y en Github.

↑ Sumario