Introdução
O preprocessador C é uma das fontes mais comuns de bugs sutis em projetos C. Macros operam por substituição textual, sem type checking, sem escopo, e com efeitos colaterais difíceis de prever. Zig substitui todo o preprocessador por comptime — código Zig normal executado em tempo de compilação.
Este guia converte os padrões mais comuns de macros C para equivalentes Zig. Para metaprogramação avançada, veja Comptime em Zig e Comptime e Reflection.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja Como Instalar Zig
- Conhecimento do preprocessador C
- Familiaridade com Zig. Consulte Introdução ao Zig
Constantes #define
C
#define MAX_BUFFER 4096
#define PI 3.14159265358979
#define VERSAO "1.2.3"
#define DEBUG_MODE 1
Zig
const MAX_BUFFER: usize = 4096;
const PI: f64 = 3.14159265358979;
const VERSAO: []const u8 = "1.2.3";
const DEBUG_MODE: bool = true;
Vantagem: constantes Zig têm tipo, são verificadas pelo compilador, e aparecem no debugger.
Macros de Função
Macro simples
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define ABS(x) ((x) < 0 ? -(x) : (x))
// Bug clássico:
int resultado = MIN(i++, j++); // i++ ou j++ avaliado DUAS vezes!
fn min(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
return if (a < b) a else b;
}
fn max(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
return if (a > b) a else b;
}
fn abs(x: anytype) @TypeOf(x) {
return if (x < 0) -x else x;
}
// Sem bug: argumentos avaliados UMA vez
var i: i32 = 5;
var j: i32 = 10;
const resultado = min(blk: { i += 1; break :blk i; }, blk: { j += 1; break :blk j; });
ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
int numeros[] = {1, 2, 3, 4, 5};
size_t n = ARRAY_SIZE(numeros); // 5
// Bug: não funciona com ponteiros
void processar(int arr[]) {
size_t n = ARRAY_SIZE(arr); // BUG: retorna sizeof(ponteiro)!
}
// Em Zig, slices carregam o tamanho
const numeros = [_]i32{ 1, 2, 3, 4, 5 };
const n = numeros.len; // 5
fn processar(arr: []const i32) void {
const n = arr.len; // Sempre correto
_ = n;
}
STRINGIFY e CONCAT
#define STRINGIFY(x) #x
#define CONCAT(a, b) a ## b
// Zig: usar comptime para geração de nomes
fn stringify(comptime valor: anytype) []const u8 {
return @typeName(@TypeOf(valor));
}
// Para concatenação de strings em comptime:
fn nomeCompleto(comptime prefixo: []const u8, comptime sufixo: []const u8) []const u8 {
return prefixo ++ sufixo;
}
const NOME = nomeCompleto("meu_", "campo"); // "meu_campo"
Compilação Condicional
C
#ifdef DEBUG
#define LOG(msg) printf("DEBUG: %s\n", msg)
#else
#define LOG(msg) ((void)0)
#endif
#if defined(__linux__)
#include <sys/epoll.h>
#elif defined(__APPLE__)
#include <sys/event.h>
#elif defined(_WIN32)
#include <winsock2.h>
#endif
#ifndef TIMEOUT
#define TIMEOUT 30
#endif
Zig
const builtin = @import("builtin");
const std = @import("std");
fn log(comptime msg: []const u8) void {
if (builtin.mode == .Debug) {
std.debug.print("DEBUG: {s}\n", .{msg});
}
// Em release, esta função compila para noop
}
// Plataforma
const os_api = if (builtin.os.tag == .linux)
@import("linux_epoll.zig")
else if (builtin.os.tag == .macos)
@import("macos_kqueue.zig")
else if (builtin.os.tag == .windows)
@import("windows_iocp.zig")
else
@compileError("OS não suportado");
// Valor padrão (sem #ifndef)
const TIMEOUT: u32 = if (@hasDecl(@import("config.zig"), "timeout"))
@import("config.zig").timeout
else
30;
Header Guards
C
#ifndef MINHA_LIB_H
#define MINHA_LIB_H
typedef struct {
int x, y;
} Ponto;
int somar(int a, int b);
#endif // MINHA_LIB_H
Zig
// Não necessário em Zig!
// Cada arquivo é um módulo — não existe inclusão dupla
// minha_lib.zig
pub const Ponto = struct {
x: i32,
y: i32,
};
pub fn somar(a: i32, b: i32) i32 {
return a + b;
}
Zig elimina completamente a necessidade de header guards. O sistema de módulos garante que cada arquivo é processado uma vez.
Macros com Variadic Arguments
C
#define LOG_ERROR(fmt, ...) fprintf(stderr, "ERRO: " fmt "\n", ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) fprintf(stdout, "INFO: " fmt "\n", ##__VA_ARGS__)
LOG_ERROR("Conexão falhou: %s", strerror(errno));
Zig
fn logError(comptime fmt: []const u8, args: anytype) void {
std.debug.print("ERRO: " ++ fmt ++ "\n", args);
}
fn logInfo(comptime fmt: []const u8, args: anytype) void {
std.debug.print("INFO: " ++ fmt ++ "\n", args);
}
logError("Conexão falhou: {s}", .{mensagem_erro});
Veja Error Logging para padrões de log mais avançados.
Type-Generic Macros (_Generic)
C11
#define imprimir(x) _Generic((x), \
int: imprimir_int, \
float: imprimir_float, \
char*: imprimir_string \
)(x)
Zig
fn imprimir(valor: anytype) void {
const T = @TypeOf(valor);
switch (@typeInfo(T)) {
.int => std.debug.print("{}\n", .{valor}),
.float => std.debug.print("{d:.2}\n", .{valor}),
.pointer => |p| {
if (p.child == u8) {
std.debug.print("{s}\n", .{valor});
}
},
else => std.debug.print("{any}\n", .{valor}),
}
}
Macros para Geração de Código (X-Macros)
C
#define ERROS \
X(OK, "Sucesso") \
X(NOT_FOUND, "Não encontrado") \
X(TIMEOUT, "Timeout") \
X(PERMISSION, "Sem permissão")
// Gerar enum
typedef enum {
#define X(nome, desc) ERRO_##nome,
ERROS
#undef X
} Erro;
// Gerar função de string
const char* erro_para_string(Erro e) {
switch (e) {
#define X(nome, desc) case ERRO_##nome: return desc;
ERROS
#undef X
}
}
Zig
const ErroInfo = struct {
nome: []const u8,
descricao: []const u8,
};
const erros = [_]ErroInfo{
.{ .nome = "OK", .descricao = "Sucesso" },
.{ .nome = "NOT_FOUND", .descricao = "Não encontrado" },
.{ .nome = "TIMEOUT", .descricao = "Timeout" },
.{ .nome = "PERMISSION", .descricao = "Sem permissão" },
};
const Erro = enum {
ok,
not_found,
timeout,
permission,
pub fn descricao(self: Erro) []const u8 {
return erros[@intFromEnum(self)].descricao;
}
};
Conclusão
O comptime de Zig substitui todo o preprocessador C com vantagens claras: tipagem, escopo, debugging, e sem efeitos colaterais surpresa. A maioria das macros C se traduz diretamente para funções Zig com anytype ou comptime, resultando em código mais seguro e mais legível.
Para mais sobre comptime, veja Comptime em Zig. Para a migração completa de C para Zig, consulte Guia de Migração: C para Zig e Como Migrar um Projeto C para Zig.