Adapter em Zig
O padrão Adapter permite que interfaces incompatíveis trabalhem juntas, convertendo a interface de uma struct em outra que o código cliente espera. Em Zig, esse padrão é muito usado para envolver APIs C em interfaces idiomáticas Zig, ou para unificar diferentes implementações sob uma interface comum.
Quando Usar
- Wrapping de bibliotecas C para interface idiomática Zig
- Unificar diferentes fontes de dados sob mesma interface
- Converter entre formatos de dados incompatíveis
- Integrar bibliotecas de terceiros com API diferente da esperada
Adapter para API C
const std = @import("std");
const c = @cImport(@cInclude("stdio.h"));
// API C retorna ponteiro ou NULL, usa errno
// Adapter converte para interface Zig com error unions
const ArquivoAdaptado = struct {
handle: *c.FILE,
const Erro = error{
FalhaAoAbrir,
FalhaAoLer,
FalhaAoEscrever,
};
pub fn abrir(caminho: [:0]const u8, modo: [:0]const u8) Erro!ArquivoAdaptado {
const handle = c.fopen(caminho.ptr, modo.ptr);
if (handle == null) return error.FalhaAoAbrir;
return .{ .handle = handle.? };
}
pub fn fechar(self: *ArquivoAdaptado) void {
_ = c.fclose(self.handle);
}
pub fn escrever(self: *ArquivoAdaptado, dados: []const u8) Erro!usize {
const escrito = c.fwrite(dados.ptr, 1, dados.len, self.handle);
if (escrito == 0) return error.FalhaAoEscrever;
return escrito;
}
pub fn ler(self: *ArquivoAdaptado, buffer: []u8) Erro!usize {
const lido = c.fread(buffer.ptr, 1, buffer.len, self.handle);
if (lido == 0 and c.ferror(self.handle) != 0) return error.FalhaAoLer;
return lido;
}
};
pub fn main() !void {
var arquivo = try ArquivoAdaptado.abrir("/tmp/teste.txt", "w");
defer arquivo.fechar();
_ = try arquivo.escrever("Olá do adapter!\n");
}
Adapter de Interface Genérica
const std = @import("std");
// Interface comum
const FonteDados = struct {
ptr: *anyopaque,
lerFn: *const fn (*anyopaque, []u8) ?[]const u8,
pub fn ler(self: FonteDados, buffer: []u8) ?[]const u8 {
return self.lerFn(self.ptr, buffer);
}
};
// Adaptar ArrayList para FonteDados
const ArrayListAdapter = struct {
lista: *std.ArrayList([]const u8),
indice: usize = 0,
pub fn fonteDados(self: *ArrayListAdapter) FonteDados {
return .{
.ptr = @ptrCast(self),
.lerFn = @ptrCast(&proximo),
};
}
fn proximo(self: *ArrayListAdapter, buffer: []u8) ?[]const u8 {
_ = buffer;
if (self.indice >= self.lista.items.len) return null;
const item = self.lista.items[self.indice];
self.indice += 1;
return item;
}
};
// Adaptar slice estático para FonteDados
const SliceAdapter = struct {
dados: []const []const u8,
indice: usize = 0,
pub fn fonteDados(self: *SliceAdapter) FonteDados {
return .{
.ptr = @ptrCast(self),
.lerFn = @ptrCast(&proximo),
};
}
fn proximo(self: *SliceAdapter, buffer: []u8) ?[]const u8 {
_ = buffer;
if (self.indice >= self.dados.len) return null;
const item = self.dados[self.indice];
self.indice += 1;
return item;
}
};
// Código cliente trabalha com FonteDados sem saber a origem
fn processarDados(fonte: FonteDados) void {
var buf: [1024]u8 = undefined;
while (fonte.ler(&buf)) |dado| {
std.debug.print("Dado: {s}\n", .{dado});
}
}
Quando Evitar
- Quando as interfaces já são compatíveis
- Quando um simples wrapper de função resolve
- Se o adapter adiciona complexidade sem benefício claro
- Quando é possível modificar a interface original
Veja Também
- Facade — Simplificar interface complexa
- Decorator — Adicionar comportamento sem alterar interface
- Interop com C — Wrapping de APIs C
- Type Erasure — Apagar tipo para interface comum
- Troubleshooting: Link C — Problemas de linkagem