Réflexion des macros C dans Zig
(jstrieb.github.io)Réflexion des macros C dans Zig
-
Zig
- Zig est un nouveau langage de programmation centré sur la programmation bas niveau et système, qui s’impose comme une alternative possible à C
- Il est encore en cours de développement, mais il est déjà utilisé dans des projets comme Bun et TigerBeetle
- L’une des fonctionnalités les plus impressionnantes de Zig est son excellente interopérabilité avec C
-
Appel de bibliothèques externes
- Dans Zig, il est facile d’appeler des bibliothèques externes
- Exemple de code :
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); }
-
Importation de fichiers d’en-tête C
- Dans Zig, on peut importer des fichiers d’en-tête C et les utiliser comme des imports Zig classiques
- Exemple de code :
const win32 = @cImport({ @cInclude("windows.h"); @cInclude("winuser.h"); }); pub fn main() !void { _ = win32.MessageBoxA(null, "world!", "Hello", 0); }
-
Programmation Windows
- Une application Windows classique possède une fonction
mainet une fonction window procedure - La fonction
maininitialise l’application et exécute une boucle qui transmet les messages à la window procedure - La window procedure reçoit les messages et les traite
- Exemple de code :
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; }
- Une application Windows classique possède une fonction
-
Réflexion
- Mapper des macros C peut être fastidieux
- Dans Zig, la fonction
@typeInfopermet d’énumérer les champs et les déclarations d’une structure - Cela permet de refléter les macros C dans Zig
- Exemple de code :
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); }
-
Conclusion
- Zig permet d’accomplir les tâches de C de manière plus pratique, avec une structure de langage de programmation plus moderne
- Zig inclut une toolchain de compilateur C, ce qui permet d’intégrer de façon fluide les déclarations des fichiers d’en-tête C
- La philosophie pragmatique de Zig apparaît clairement dès qu’on commence à apprendre le langage
- La conception intuitive et cohérente de Zig contribue à améliorer la productivité
Le résumé de GN⁺
- Zig est un nouveau langage centré sur la programmation bas niveau et système, remarquable par son excellente interopérabilité avec C
- Zig peut importer et utiliser des fichiers d’en-tête C, et permet aussi de refléter les macros C dans Zig
- La philosophie pragmatique et la conception intuitive de Zig aident grandement à apprendre et à utiliser le langage
- Zig offre une voie pour faire évoluer des bases de code C existantes vers Zig, ce qui réduit les obstacles à son adoption
1 commentaires
Commentaire Hacker News
La fonctionnalité
@cImportdevrait être suppriméeExemple de code :
Code équivalent en D :
Le compilateur se charge du reste
Certaines personnes demandent une syntaxe spéciale pour importer des fichiers C, mais cette simplicité est préférable
J’aimerais aimer Zig, mais je rencontre quelques problèmes
zig initcontient beaucoup de code inutilezig build-exe filename.zigLe préprocesseur de Clang n’est pas implémenté comme une étape séparée avant la compilation
J’ai écrit un billet de blog sur la façon de faire quelque chose de similaire avec ImportC dans le langage D
Il semble que cela ajouterait au minimum
UINT16_MAX*sizeof(intptr_t)octets à l’exécutable pour chaque enumLa définition des fonctions paraît très lisible
J’aime bien le site