zig translate-c es una herramienta que traduce declaraciones y código C a Zig. En Zig 0.16 el traductor se basa en Aro, no en libclang, y comparte la infraestructura de traducción usada por addTranslateC.
Zig incluye una herramienta de línea de comandos integrada llamada translate-c.
No es un simple “buscar y reemplazar”. La herramienta analiza tipos, macros y punteros de C e intenta emitir código Zig con comportamiento compatible. La traducción es de mejor esfuerzo: ciertos elementos se degradan a declaraciones extern, tipos opacos o @compileError.
Cómo usarla
Desde la terminal usamos:
zig translate-c main.c > main.zigEsto toma el archivo main.c, lo traduce y vuelca el resultado en main.zig.
También puedes pasarle flags de compilación de C si tu código depende de includes especiales o definiciones:
zig translate-c -I./include -DDEBUG=1 main.cEjemplo práctico
Vamos a ver qué hace exactamente. Supongamos que tenemos este código C (math.c):
// math.c
#include <stdint.h>
#define MAX_VAL 100
struct Vector2 {
float x;
float y;
};
int sumar_vectores(struct Vector2* a, struct Vector2* b) {
if (a->x > MAX_VAL) return -1;
return (int)(a->x + b->x);
}Si ejecutamos zig translate-c math.c, obtendremos un resultado parecido a este (el texto exacto puede cambiar entre versiones):
pub const MAX_VAL = @as(c_int, 100);
pub const struct_Vector2 = extern struct {
x: f32,
y: f32,
};
pub export fn sumar_vectores(a: [*c]struct_Vector2, b: [*c]struct_Vector2) c_int {
if (a.*.x > @as(f32, @floatFromInt(MAX_VAL))) {
return -1;
}
return @as(c_int, @intFromFloat(a.*.x + b.*.x));
}Fíjate en lo que ha pasado.
- Macros:
#define MAX_VALse ha convertido en una constantepub const. - Structs:
struct Vector2es ahora unextern struct, garantizando que el layout en memoria sea idéntico al de C. - Tipos:
intha pasado a serc_int(noi32, para mantener compatibilidad pura). - Punteros: Los punteros de C se representan habitualmente como
[*c]T, porque la cabecera no expresa longitud ni nulabilidad con los tipos de Zig. - Casteos: Zig es estricto. El traductor ha añadido
@as,@floatFromInty@intFromFloatpara replicar los casteos implícitos que C hacía silenciosamente.
¿Para qué sirve realmente?
El código resultante suele ser “Zig con acento de C”. No es necesariamente idiomático: conserva tipos c_int, punteros de C y conversiones necesarias para mantener el comportamiento original.
Entonces, ¿por qué es tan útil?
Consultar cómo se representa una declaración
Si dudas sobre cómo representa Zig una declaración de C, puedes traducir una cabecera mínima e inspeccionar el resultado. Recuerda que esa representación prioriza la compatibilidad, no el diseño idiomático.
Diagnosticar la traducción de una cabecera
addTranslateC, zig translate-c y el deprecado @cImport comparten la misma base de traducción.
Si aparece un error al traducir una biblioteca, ejecutar zig translate-c libreria.h manualmente permite inspeccionar qué declaraciones se convirtieron y cuáles terminaron como @compileError u opaque.
Crear un punto de partida para portar
Si tienes que migrar un archivo de 5.000 líneas de C, traducirlo a mano es tedioso.
Podemos usar translate-c como primer borrador y después refactorizar función por función: sustituir [*c]T por slices cuando conozcamos la longitud, expresar la propiedad y cambiar códigos de error por !T. No hay que asumir que toda traducción compilará sin ajustes.
Limitaciones
translate-c no hace milagros. Hay cosas de C que son intraducibles directamente o generan código muy feo:
- Macros complejas: Las macros que abusan del preprocesador (concatenar tokens, bucles ocultos) a menudo se ignoran o generan errores.
- Punteros
void*: se traducen a punteros opacos compatibles, cuya intención tendrás que recuperar a partir del contrato de C. gotoy otras construcciones no traducibles: una función puede quedar degradada a una declaraciónextern, de modo que todavía puedas enlazar y llamarla desde código compilado como C.