← Volver al listado de tecnologías
Interoperabilidad con C
Interoperabilidad con C
Importar Headers C
const std = @import("std");
// Importar biblioteca estándar de C
const c = @cImport({
@cInclude("stdio.h");
@cInclude("stdlib.h");
@cInclude("string.h");
});
pub fn main() void {
// Usar printf de C
_ = c.printf("Hola desde C! %d\n", @as(c_int, 42));
// Usar strlen
const texto = "Zig y C juntos";
const len = c.strlen(texto);
std.debug.print("Longitud: {d}\n", .{len});
}
Tipos C en Zig
const std = @import("std");
// Equivalencias de tipos
const mi_int: c_int = 42; // int
const mi_long: c_long = 100; // long
const mi_uint: c_uint = 255; // unsigned int
const mi_char: u8 = 'A'; // char
const mi_size: usize = 1024; // size_t
// Punteros
const c_string: [*:0]const u8 = "Hola C"; // char*
const void_ptr: ?*anyopaque = null; // void*
pub fn main() void {
std.debug.print("int: {d}, long: {d}\n", .{ mi_int, mi_long });
}
Llamar Funciones C
const std = @import("std");
const c = @cImport({
@cInclude("math.h");
});
pub fn main() void {
// Funciones matemáticas de C
const x: f64 = 2.0;
const raiz = c.sqrt(x);
const seno = c.sin(std.math.pi / 2.0);
const potencia = c.pow(x, 3.0);
std.debug.print("sqrt(2) = {d}\n", .{raiz});
std.debug.print("sin(π/2) = {d}\n", .{seno});
std.debug.print("2³ = {d}\n", .{potencia});
}
Exportar Funciones a C
const std = @import("std");
// Función exportada con convención C
export fn sumar_c(a: c_int, b: c_int) c_int {
return a + b;
}
// Con nombre personalizado
export fn zig_multiplicar(a: c_int, b: c_int) c_int {
return a * b;
}
// Callback compatible con C
export fn mi_callback(datos: ?*anyopaque) callconv(.C) void {
if (datos) |ptr| {
const valor: *i32 = @ptrCast(@alignCast(ptr));
std.debug.print("Callback con valor: {d}\n", .{valor.*});
}
}
Structs Compatibles con C
const std = @import("std");
// Struct con layout C
const Punto = extern struct {
x: f64,
y: f64,
};
const Rectangulo = extern struct {
origen: Punto,
ancho: f64,
alto: f64,
};
// Packed para control exacto de bits
const Flags = packed struct {
activo: bool,
visible: bool,
seleccionado: bool,
_padding: u5,
};
export fn crear_punto(x: f64, y: f64) Punto {
return Punto{ .x = x, .y = y };
}
export fn area_rectangulo(rect: *const Rectangulo) f64 {
return rect.ancho * rect.alto;
}
pub fn main() void {
const p = crear_punto(10.0, 20.0);
std.debug.print("Punto: ({d}, {d})\n", .{ p.x, p.y });
const rect = Rectangulo{
.origen = p,
.ancho = 100,
.alto = 50,
};
std.debug.print("Área: {d}\n", .{area_rectangulo(&rect)});
}
Manejo de Memoria con C
const std = @import("std");
const c = @cImport({
@cInclude("stdlib.h");
@cInclude("string.h");
});
pub fn main() !void {
// Asignar memoria con malloc de C
const size: usize = 100;
const ptr: [*]u8 = @ptrCast(c.malloc(size) orelse return error.OutOfMemory);
defer c.free(ptr);
// Inicializar con memset
_ = c.memset(ptr, 0, size);
// Copiar datos
const mensaje = "Hola desde Zig";
_ = c.memcpy(ptr, mensaje.ptr, mensaje.len);
// Convertir a slice de Zig
const slice: []u8 = ptr[0..mensaje.len];
std.debug.print("Contenido: {s}\n", .{slice});
}
Allocator que usa C
const std = @import("std");
const c = @cImport({
@cInclude("stdlib.h");
});
const c_allocator = std.mem.Allocator{
.ptr = undefined,
.vtable = &.{
.alloc = cAlloc,
.resize = cResize,
.free = cFree,
},
};
fn cAlloc(_: *anyopaque, len: usize, _: u8, _: usize) ?[*]u8 {
return @ptrCast(c.malloc(len));
}
fn cResize(_: *anyopaque, _: []u8, _: u8, _: usize, _: usize) bool {
return false; // C no soporta resize in-place
}
fn cFree(_: *anyopaque, buf: []u8, _: u8, _: usize) void {
c.free(buf.ptr);
}
pub fn main() !void {
// Usar el allocator de C con estructuras de Zig
var lista = std.ArrayList(i32).init(c_allocator);
defer lista.deinit();
try lista.append(1);
try lista.append(2);
try lista.append(3);
std.debug.print("Lista: {any}\n", .{lista.items});
}
Testing de Interop
const std = @import("std");
const testing = std.testing;
const c = @cImport({
@cInclude("string.h");
@cInclude("stdlib.h");
});
const Punto = extern struct {
x: f64,
y: f64,
pub fn distancia(self: Punto, otro: Punto) f64 {
const dx = self.x - otro.x;
const dy = self.y - otro.y;
return @sqrt(dx * dx + dy * dy);
}
};
fn duplicarStringC(s: [*:0]const u8) ![]u8 {
const len = c.strlen(s);
const allocator = testing.allocator;
const copia = try allocator.alloc(u8, len);
_ = c.memcpy(copia.ptr, s, len);
return copia;
}
fn compararStringsC(a: []const u8, b: []const u8) bool {
if (a.len != b.len) return false;
return c.memcmp(a.ptr, b.ptr, a.len) == 0;
}
test "tipos C básicos" {
const i: c_int = 42;
const l: c_long = 1000;
try testing.expectEqual(@as(c_int, 42), i);
try testing.expectEqual(@as(c_long, 1000), l);
}
test "strlen de C" {
const texto: [*:0]const u8 = "Hola mundo";
const len = c.strlen(texto);
try testing.expectEqual(@as(usize, 10), len);
}
test "memcmp de C" {
const a = "abc";
const b = "abc";
const d = "abd";
try testing.expectEqual(@as(c_int, 0), c.memcmp(a.ptr, b.ptr, 3));
try testing.expect(c.memcmp(a.ptr, d.ptr, 3) < 0);
}
test "extern struct layout" {
// Verificar que el struct tiene layout de C
try testing.expectEqual(@as(usize, 16), @sizeOf(Punto));
const p = Punto{ .x = 3.0, .y = 4.0 };
try testing.expectEqual(@as(f64, 3.0), p.x);
}
test "método en extern struct" {
const p1 = Punto{ .x = 0, .y = 0 };
const p2 = Punto{ .x = 3, .y = 4 };
try testing.expectEqual(@as(f64, 5.0), p1.distancia(p2));
}
test "duplicar string con funciones C" {
const original: [*:0]const u8 = "Test string";
const copia = try duplicarStringC(original);
defer testing.allocator.free(copia);
try testing.expectEqualStrings("Test string", copia);
}
test "comparar strings con memcmp" {
try testing.expect(compararStringsC("hola", "hola"));
try testing.expect(!compararStringsC("hola", "mundo"));
try testing.expect(!compararStringsC("hola", "hol"));
}
Build con Bibliotecas C
En tu build.zig:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "mi_app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// Linkear con biblioteca C
exe.linkLibC();
// Linkear biblioteca específica
exe.linkSystemLibrary("m"); // libm (math)
exe.linkSystemLibrary("pthread");
// Agregar ruta de includes
exe.addIncludePath(b.path("include"));
b.installArtifact(exe);
}
Ejercicios
- Crea un wrapper de Zig para una función C de tu elección
- Implementa un struct que sea compatible con una estructura C existente
- Escribe una biblioteca en Zig que pueda ser llamada desde C
Resumen
@cImportimporta headers C directamenteextern structgarantiza layout compatible con Cexport fnexpone funciones a C- Los tipos C tienen equivalentes:
c_int,c_long, etc. callconv(.C)especifica la convención de llamada C