@cImport em Zig — Referência e Exemplos

@cImport / @cInclude em Zig

O @cImport importa cabeçalhos C diretamente para Zig, permitindo chamar funções C sem escrever bindings manuais. Dentro do bloco @cImport, use @cInclude para incluir headers, @cDefine para definir macros e @cUndef para desdefini-las. O compilador Zig traduz automaticamente as declarações C para tipos Zig equivalentes.

Sintaxe

@cImport(comptime expr: void) type
@cInclude(comptime path: []const u8) void
@cDefine(comptime name: []const u8, comptime value: []const u8) void
@cUndef(comptime name: []const u8) void

Parâmetros

  • @cImport: Recebe um bloco de expressões comptime que configuram quais headers importar.
  • @cInclude: Caminho do header C a incluir (como #include do C).
  • @cDefine: Define uma macro (como #define do C).
  • @cUndef: Remove definição de macro.

Valor de retorno

@cImport retorna um type — a struct que contém todas as declarações públicas traduzidas dos headers C.

Exemplos práticos

Exemplo 1: Importar a biblioteca C padrão

const std = @import("std");
const c = @cImport(@cInclude("stdio.h"));

pub fn main() void {
    _ = c.printf("Hello from C! %d\n", @as(c_int, 42));
}

Exemplo 2: Usar math.h

const std = @import("std");
const c = @cImport({
    @cInclude("math.h");
    @cInclude("stdlib.h");
});

pub fn main() void {
    const raiz = c.sqrt(144.0);
    std.debug.print("sqrt(144) = {d}\n", .{raiz}); // 12.0

    const absoluto = c.abs(-42);
    std.debug.print("abs(-42) = {}\n", .{absoluto}); // 42
}

Exemplo 3: Importar biblioteca externa com defines

const c = @cImport({
    @cDefine("NCURSES_WIDECHAR", "1");
    @cInclude("ncurses.h");
});

pub fn main() void {
    _ = c.initscr();
    _ = c.printw("Hello, ncurses from Zig!\n");
    _ = c.refresh();
    _ = c.getch();
    _ = c.endwin();
}

Configuração no build.zig

Para usar @cImport com bibliotecas externas, configure os caminhos no 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 = "app",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Linkar com biblioteca C
    exe.linkLibC();
    exe.linkSystemLibrary("ncurses");

    // Adicionar caminhos de include se necessário
    exe.addIncludePath(b.path("include"));

    b.installArtifact(exe);
}

Funções Auxiliares

FunçãoDescrição
@cImport(...)Iniciar bloco de importação C
@cInclude("header.h")Incluir header (como #include)
@cDefine("MACRO", "valor")Definir macro de pré-processador
@cUndef("MACRO")Remover definição de macro

Casos de uso comuns

  1. Bibliotecas C existentes: Usar SQLite, OpenSSL, SDL, etc. diretamente de Zig.
  2. APIs do sistema: Acessar syscalls e interfaces do OS.
  3. Migração gradual: Chamar código C legado enquanto migra para Zig.
  4. Protótipos rápidos: Usar bibliotecas C sem escrever wrappers manuais.

Como o @cImport funciona internamente

O Zig invoca o Clang como biblioteca para parsear os cabeçalhos C e gera um módulo Zig equivalente. Esse processo acontece em tempo de compilação. O módulo gerado fica em cache e não precisa ser reprocessado a cada compilação se os headers não mudarem.

Tipos C são mapeados para tipos Zig equivalentes:

Tipo CTipo Zig
intc_int
unsigned intc_uint
longc_long
char *[*c]u8
void *?*anyopaque
size_tusize
bool (C99)bool
ponteiro de funçãoponteiro de função Zig

Lidando com ponteiros C (*[c])

O tipo [*c]T representa ponteiros C — que podem ser null, unitários ou apontar para arrays. Ao usar funções C que retornam esses ponteiros, verifique null antes de usar:

const c = @cImport(@cInclude("string.h"));

pub fn main() !void {
    const str = "hello world";
    const pos = c.strchr(str.ptr, 'w');

    if (pos == null) {
        return error.NaoEncontrado;
    }

    // pos é [*c]u8, não null, usar com cuidado
    const resultado: []const u8 = std.mem.span(pos);
    _ = resultado;
}

Macros C e limitações

@cImport lida com funções C e constantes, mas macros C complexas não são traduzidas automaticamente. Macros simples que definem constantes funcionam. Macros que são pseudo-funções frequentemente falham:

// Isso funciona: macro que define constante
// #define MAX_SIZE 1024
// -> c.MAX_SIZE == 1024

// Isso pode não funcionar: macro-função
// #define MIN(a, b) ((a) < (b) ? (a) : (b))
// -> c.MIN pode não existir

// Solução: reescrever em Zig
fn min(a: anytype, b: anytype) @TypeOf(a, b) {
    return if (a < b) a else b;
}

Use zig translate-c header.h na linha de comando para ver como o Zig traduz um header e identificar o que está disponível.

Boas práticas

Isolar o @cImport em um único arquivo: Em vez de espalhar @cImport pelo código, crie um arquivo c.zig com todas as importações C e importe esse módulo nos outros arquivos. Isso facilita manutenção e compilação incremental.

// c.zig
pub const c = @cImport({
    @cInclude("sqlite3.h");
    @cInclude("openssl/ssl.h");
});

// main.zig
const c = @import("c.zig").c;

Erros comuns

Esquecer de linkar a biblioteca: @cImport resolve os tipos, mas você ainda precisa de exe.linkSystemLibrary("nome") no build.zig para que o linker encontre as implementações.

Usar ponteiros C sem verificar null: Funções C retornam [*c]T ou ?*T que podem ser null. Verifique sempre antes de dereferenciar.

Perguntas Frequentes

Posso usar @cImport com headers locais do meu projeto?

Sim. Use exe.addIncludePath(b.path("include")) no build.zig para adicionar diretórios de include, e então @cInclude("meu_header.h") funcionará normalmente.

@cImport é mais lento que bindings manuais?

Em termos de performance do binário gerado, não há diferença — o código C compilado é o mesmo. O tempo de compilação pode ser um pouco maior na primeira vez, mas o resultado é cacheado.

Builtins relacionados

  • @import — Importar módulos Zig
  • @embedFile — Incorporar arquivos no binário
  • @ptrCast — Converter ponteiros entre tipos Zig e C

Tutoriais relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.