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:
Filtro para transformar el fichero en código fuente estándar C:
cedro programa.c >programa-estándar.c
Envoltorio que ejecuta automáticamente el compilador cc
:
cedrocc programa.c -o programa
¿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 |
|
#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ódigo | Compilar con GCC 11 | Compilar con clang 12 | ||||
---|---|---|---|---|---|---|
cedro | 0.23 s | 1.56 MB | 27.66 s | 1328.13 MB | 30.44 s | 984.59 MB |
cedro --embed-as-string=… | 0.17 s | 1.87 MB | 0.99 s | 125.32 MB | 0.42 s | 144.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:
@
[«backstitch» en inglés].auto ...
o defer ...
.break etiqueta;
.ristra[inicio..fin]
.#define { ... #define }
.#foreach { ... #foreach }
.#include {...}
/#embed "..."
.12'34
| 12_34
→ 1234
, 0b1010
→ 0xA
).Cedro puede descargarse, bajo la licencia Apache 2.0, en su página web y en Github.