Reflexão de macros C no Zig
(jstrieb.github.io)Reflexão de macros C no Zig
-
Zig
- Zig é uma nova linguagem de programação focada em programação de baixo nível e de sistemas, posicionando-se como uma linguagem capaz de substituir C
- Ainda está em desenvolvimento, mas já é usada em projetos como Bun e TigerBeetle
- Um dos recursos mais impressionantes do Zig é sua excelente interoperabilidade com C
-
Chamando bibliotecas externas
- No Zig, é possível chamar bibliotecas externas com facilidade
- Código de exemplo:
const win = @import("std").os.windows; extern "user32" fn MessageBoxA(?win.HWND, [*:0]const u8, [*:0]const u8, u32,) callconv(win.WINAPI) i32; pub fn main() !void { _ = MessageBoxA(null, "world!", "Hello", 0); }
-
Importando arquivos de cabeçalho C
- No Zig, é possível importar arquivos de cabeçalho C e usá-los como um import comum do Zig
- Código de exemplo:
const win32 = @cImport({ @cInclude("windows.h"); @cInclude("winuser.h"); }); pub fn main() !void { _ = win32.MessageBoxA(null, "world!", "Hello", 0); }
-
Programação para Windows
- Um aplicativo Windows típico tem uma função main e uma função window procedure
- A função main inicializa o aplicativo e executa um loop que encaminha mensagens para a window procedure
- A window procedure recebe e processa as mensagens
- Código de exemplo:
const std = @import("std"); const windows = std.os.windows; const win32 = @cImport({ @cInclude("windows.h"); @cInclude("winuser.h"); }); var stdout: std.fs.File.Writer = undefined; pub export fn WindowProc(hwnd: win32.HWND, uMsg: c_uint, wParam: win32.WPARAM, lParam: win32.LPARAM) callconv(windows.WINAPI) win32.LRESULT { _ = switch (uMsg) { win32.WM_CLOSE => win32.DestroyWindow(hwnd), win32.WM_DESTROY => win32.PostQuitMessage(0), else => { stdout.print("Unknown window message: 0x{x:0>4}\n", .{uMsg}) catch undefined; }, }; return win32.DefWindowProcA(hwnd, uMsg, wParam, lParam); } pub export fn main(hInstance: win32.HINSTANCE) c_int { stdout = std.io.getStdOut().writer(); var class = std.mem.zeroes(win32.WNDCLASSEXA); class.cbSize = @sizeOf(win32.WNDCLASSEXA); class.style = win32.CS_VREDRAW | win32.CS_HREDRAW; class.hInstance = hInstance; class.lpszClassName = "Class"; class.lpfnWndProc = WindowProc; _ = win32.RegisterClassExA(&class); const hwnd = win32.CreateWindowExA(win32.WS_EX_CLIENTEDGE, "Class", "Window", win32.WS_OVERLAPPEDWINDOW, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, null, null, hInstance, null); _ = win32.ShowWindow(hwnd, win32.SW_NORMAL); _ = win32.UpdateWindow(hwnd); var message: win32.MSG = std.mem.zeroes(win32.MSG); while (win32.GetMessageA(&message, null, 0, 0) > 0) { _ = win32.TranslateMessage(&message); _ = win32.DispatchMessageA(&message); } return 0; }
-
Reflexão
- Mapear macros C pode ser trabalhoso
- No Zig, é possível listar campos e declarações de structs usando a função
@typeInfo - Com isso, dá para refletir macros C no Zig
- Código de exemplo:
const window_messages = get_window_messages(); fn get_window_messages() [65536][:0]const u8 { var result: [65536][:0]const u8 = undefined; @setEvalBranchQuota(1000000); for (@typeInfo(win32).Struct.decls) |field| { if (field.name.len >= 3 and std.mem.eql(u8, field.name[0..3], "WM_")) { const value = @field(win32, field.name); result[value] = field.name; } } return result; } pub export fn WindowProc(hwnd: win32.HWND, uMsg: c_uint, wParam: win32.WPARAM, lParam: win32.LPARAM) callconv(windows.WINAPI) win32.LRESULT { _ = switch (uMsg) { win32.WM_CLOSE => win32.DestroyWindow(hwnd), win32.WM_DESTROY => win32.PostQuitMessage(0), else => { stdout.print("{s}: 0x{x:0>4}\n", .{ window_messages[uMsg], uMsg }) catch undefined; }, }; return win32.DefWindowProcA(hwnd, uMsg, wParam, lParam); }
-
Conclusão
- O Zig consegue realizar as mesmas tarefas do C de forma mais conveniente, usando estruturas de linguagem mais modernas
- O Zig inclui um toolchain de compilador C, permitindo incorporar sem atrito as declarações de arquivos de cabeçalho C
- A filosofia pragmática do Zig fica evidente assim que se começa a aprender a linguagem
- O design intuitivo e consistente do Zig contribui para aumentar a produtividade
Resumo do GN⁺
- Zig é uma nova linguagem focada em programação de baixo nível e de sistemas, com excelente interoperabilidade com C
- Zig pode importar arquivos de cabeçalho C para uso direto e também refletir macros C dentro da linguagem
- A filosofia pragmática e o design intuitivo do Zig ajudam bastante no aprendizado e no uso da linguagem
- Zig oferece um caminho para migrar bases de código C existentes para Zig, superando barreiras de adoção da linguagem
1 comentários
Comentários do Hacker News
O recurso
@cImportestá previsto para ser removidolibclangCódigo de exemplo:
Código equivalente em D:
O compilador cuida do resto
Há pessoas pedindo uma sintaxe especial para importar arquivos C, mas essa simplicidade é melhor
Quero gostar de Zig, mas estou tendo alguns problemas
zig inittem muito código desnecessáriozig build-exe filename.zigO pré-processador do Clang não é implementado como uma etapa separada antes da compilação
Escrevi no blog como fazer algo semelhante em D usando ImportC
Parece que isso adicionaria pelo menos
UINT16_MAX*sizeof(intptr_t)bytes ao executável para cada enumA definição de função parece muito fácil de ler
Gostei do site