ZigにおけるCマクロのリフレクション
(jstrieb.github.io)ZigのCマクロ反映
-
Zig
- Zigは低レベルおよびシステムプログラミングに重点を置いた新しいプログラミング言語で、Cを置き換えうる言語として位置づけられている
- 現在も開発中だが、すでにBunやTigerBeetleのようなプロジェクトで使われている
- Zigの最も印象的な機能の1つは、Cとの優れた相互運用性である
-
外部ライブラリの呼び出し
- Zigでは外部ライブラリを簡単に呼び出せる
- 例示コード:
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); }
-
Cヘッダーファイルの取り込み
- ZigではCヘッダーファイルを取り込み、通常のZigのimportのように使える
- 例示コード:
const win32 = @cImport({ @cInclude("windows.h"); @cInclude("winuser.h"); }); pub fn main() !void { _ = win32.MessageBoxA(null, "world!", "Hello", 0); }
-
Windowsプログラミング
- 一般的なWindowsアプリケーションはmain関数とwindow procedure関数を持つ
- main関数はアプリケーションを初期化し、メッセージをwindow procedureへ渡すループを実行する
- window procedureはメッセージを受け取って処理する
- 例示コード:
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; }
-
リフレクション
- Cマクロをマッピングするのは煩雑になりうる
- Zigでは
@typeInfo関数を使って構造体フィールドと宣言を列挙できる - これにより、CマクロをZig上でリフレクトできる
- 例示コード:
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); }
-
結論
- Zigでは、Cの機能をよりモダンなプログラミング言語の構造で、より便利に扱える
- ZigはCコンパイラのツールチェーンを含んでおり、Cヘッダーファイルの宣言をシームレスに取り込める
- Zigのプラグマティズムな哲学は、言語を学び始めるとすぐに見えてくる
- Zigの直感的で一貫した設計は、生産性の向上に貢献する
GN⁺の要約
- Zigは低レベルおよびシステムプログラミングに重点を置いた新しい言語で、Cとの優れた相互運用性を誇る
- ZigはCヘッダーファイルを取り込んで利用でき、CマクロをZig上でリフレクトすることもできる
- Zigのプラグマティズムな哲学と直感的な設計は、言語を学び使ううえで大いに役立つ
- Zigは既存のCコードベースをZigへ移行する道筋を提供し、言語採用の障壁を乗り越えられる
1件のコメント
Hacker News のコメント
@cImport機能は削除予定libclangへの依存をなくすため、この機能を言語から削除しようとしているサンプルコード:
D 言語での同等のコード:
コンパイラが残りを処理する
C ファイルを取り込むための特別な構文を求める人もいるが、このシンプルさのほうが良い
Zig を好きになりたいが、いくつか問題に遭遇している
zig initでプロジェクトを始める推奨方法には不要なコードが多いzig build-exe filename.zigで初期化部分を飛ばせることを知ったClang のプリプロセッサは、コンパイル前の独立した段階として実装されていない
D 言語で ImportC を使って同様のことを行う方法をブログに書いた
各
enumごとに、少なくともUINT16_MAX*sizeof(intptr_t)バイトが実行ファイルに追加されそう関数定義がとても読みやすく見える
このサイトが気に入った