FFI — Llamar código C
¿Por qué llamar a C?
Nyx es poderoso, pero el mundo funciona con C. Sistemas operativos, bibliotecas gráficas, bases de datos, algoritmos de compresión — décadas de código C probado en batalla existen. En lugar de reescribir todo, Nyx te permite llamar funciones C directamente. Esto se llama FFI — Interfaz de Funciones Foráneas.
Declarar una función externa
Para llamar una función C desde Nyx, la declaras con extern "C":
extern "C" fn abs(x: int) -> int fn main() { print(abs(-42)) // 42 print(abs(10)) // 10 }
Esto le dice al compilador: "hay una función C llamada abs que recibe un int y devuelve un int. La proporcionaré en tiempo de enlace."
La biblioteca estándar de C se enlaza automáticamente, así que funciones como abs simplemente funcionan.
Mapeo de tipos
Cuando llamas a C, los tipos deben coincidir. Así es como los tipos de Nyx se mapean a C:
| Tipo Nyx | Tipo C | LLVM IR |
|---|---|---|
int |
long long (64-bit) |
i64 |
float |
double (64-bit) |
double |
bool |
_Bool |
i1 |
String |
const char* |
i8* |
*int |
int64_t* |
i64* |
i32 |
int (32-bit) |
i32 |
i8 |
char (8-bit) |
i8 |
Cuando pasas un String de Nyx a una función C, el compilador lo convierte automáticamente a un char* terminado en null.
Llamar funciones matemáticas
extern "C" fn sqrt(x: float) -> float extern "C" fn pow(base: float, exp: float) -> float extern "C" fn log(x: float) -> float fn main() { let raiz: float = sqrt(144.0) print(raiz) // 12.0 let resultado: float = pow(2.0, 10.0) print(resultado) // 1024.0 }
Enlaza con -lm (la biblioteca matemática) al compilar.
Llamar funciones de strings
extern "C" fn strlen(s: String) -> int extern "C" fn strcmp(a: String, b: String) -> int fn main() { print(strlen("Hello")) // 5 let cmp: int = strcmp("apple", "banana") if cmp < 0 { print("apple va primero") } }
Structs compatibles con C
Para pasar structs a C, deben tener layout de memoria compatible con C. Usa #[repr(C)]:
#[repr(C)] struct Punto { x: i64, y: i64 } extern "C" fn procesar_punto(p: *Punto) -> int fn main() { var p: Punto = Punto { x: 10, y: 20 } unsafe { let resultado: int = procesar_punto(&p) } }
Sin #[repr(C)], los structs de Nyx se asignan en el heap con punteros del GC — no son compatibles con C. Con él, el struct se dispone exactamente como C espera.
Cómo el runtime usa FFI
El propio runtime de Nyx está construido sobre FFI. Cuando llamas tcp_listen, thread_spawn o print, esas son funciones C en el runtime:
// runtime/net.c
int64_t nyx_tcp_listen(const char* host, int64_t port) {
// ... crear socket, bind, listen
}
El compilador conoce estas funciones y genera llamadas a ellas. Tus declaraciones FFI usan exactamente el mismo mecanismo.
Ejemplo práctico: llamar zlib para compresión
extern "C" fn nyx_compress(data: String) -> String extern "C" fn nyx_decompress(data: String, original_size: int) -> String fn main() { let original: String = "Hola Hola Hola Hola Hola" let comprimido: String = nyx_compress(original) print("Tamaño original: " + int_to_string(original.length())) print("Tamaño comprimido: " + int_to_string(comprimido.length())) let restaurado: String = nyx_decompress(comprimido, original.length()) print("Restaurado: " + restaurado) }
Ejemplo práctico: obtener la hora
extern "C" fn time(ptr: int) -> int fn main() { let ahora: int = time(0) print("Timestamp Unix: " + int_to_string(ahora)) }
Consideraciones de seguridad
FFI es inherentemente unsafe. Las funciones C pueden:
- Crashear si reciben punteros inválidos
- Escribir más allá de los límites de un buffer
- Devolver datos inválidos
- Fugar memoria
Siempre valida las entradas antes de pasarlas a C, y prueba exhaustivamente. FFI es un puente entre Nyx seguro y C inseguro — trátalo con cuidado.
Ejercicios
- Declara y llama
atoi(s: String) -> intde la biblioteca estándar de C para convertir un string a entero. Compara constring_to_int().
- Llama
rand() -> intysrand(seed: int)de C para generar números aleatorios.
- Declara
getenv(name: String) -> Stringy úsala para leer una variable de entorno.
- Escribe un programa que llame
strlenen varios strings y compare los resultados con.length()de Nyx.
- Crea un struct
#[repr(C)]representando un rectángulo 2D y escribe una función C (en un archivo.c) que calcule su área. Llámala desde Nyx.
Resumen
extern "C" fn nombre(params) -> TipoRetornodeclara una función C.- Los tipos de Nyx se mapean a C:
int→i64,String→char*,float→double. - La conversión de strings (Nyx a C
char*) ocurre automáticamente. #[repr(C)]hace el layout del struct compatible con C.- El propio runtime de Nyx usa FFI para todas las operaciones del sistema.
- FFI es poderoso pero unsafe — valida entradas y prueba cuidadosamente.
Siguiente capítulo: Unsafe y raw pointers →