Reflexión de macros de C en Zig
(jstrieb.github.io)Reflexión de macros de C en Zig
-
Zig
- Zig es un nuevo lenguaje de programación enfocado en la programación de bajo nivel y de sistemas, y se está posicionando como un lenguaje capaz de reemplazar a C
- Aunque sigue en desarrollo, ya se usa en proyectos como Bun y TigerBeetle
- Una de las características más impresionantes de Zig es su excelente interoperabilidad con C
-
Llamada a bibliotecas externas
- En Zig se pueden llamar bibliotecas externas fácilmente
- Código de ejemplo:
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); }
-
Importación de archivos de cabecera de C
- En Zig se pueden importar archivos de cabecera de C y usarlos como si fueran importaciones normales de Zig
- Código de ejemplo:
const win32 = @cImport({ @cInclude("windows.h"); @cInclude("winuser.h"); }); pub fn main() !void { _ = win32.MessageBoxA(null, "world!", "Hello", 0); }
-
Programación para Windows
- Una aplicación típica de Windows tiene una función
mainy una función window procedure - La función
maininicializa la aplicación y ejecuta un bucle que envía mensajes a la window procedure - La window procedure recibe y procesa los mensajes
- Código de ejemplo:
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; }
- Una aplicación típica de Windows tiene una función
-
Reflexión
- Mapear macros de C puede ser tedioso
- En Zig se puede usar la función
@typeInfopara enumerar campos y declaraciones de estructuras - Esto permite reflejar macros de C dentro de Zig
- Código de ejemplo:
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); }
-
Conclusión
- Zig puede realizar las funciones de C de una forma más conveniente usando estructuras de lenguaje de programación más modernas
- Zig incluye una cadena de herramientas de compilación de C, por lo que puede incorporar sin fricción las declaraciones de archivos de cabecera de C
- La filosofía pragmática de Zig se hace evidente apenas se empieza a aprender el lenguaje
- El diseño intuitivo y consistente de Zig contribuye a mejorar la productividad
Resumen de GN⁺
- Zig es un nuevo lenguaje enfocado en la programación de bajo nivel y de sistemas, con una excelente interoperabilidad con C
- Zig puede importar y usar archivos de cabecera de C, y también puede reflejar macros de C dentro de Zig
- La filosofía pragmática y el diseño intuitivo de Zig ayudan mucho a la hora de aprender y usar el lenguaje
- Zig ofrece una ruta para migrar bases de código existentes en C hacia Zig, superando barreras para la adopción del lenguaje
1 comentarios
Comentarios de Hacker News
La función
@cImportestá por eliminarseCódigo de ejemplo:
Código equivalente en D:
El compilador se encarga del resto
Hay personas que piden una sintaxis especial para importar archivos C, pero esta simplicidad es mejor
Quiero que me guste Zig, pero me he topado con algunos problemas
zig inittrae mucho código innecesariozig build-exe filename.zigEl preprocesador de Clang no está implementado como una etapa separada previa a la compilación
Escribí en el blog cómo hacer algo similar en D usando ImportC
Parece que cada
enumañadirá al ejecutable al menosUINT16_MAX*sizeof(intptr_t)bytesLa definición de funciones se ve muy fácil de leer
Me gusta el sitio