รีเฟลกชันของ C มาโครใน Zig
(jstrieb.github.io)การสะท้อน C มาโครใน Zig
-
Zig
- Zig เป็นภาษาโปรแกรมใหม่ที่เน้นการเขียนโปรแกรมระดับล่างและระบบ และกำลังวางตำแหน่งตัวเองเป็นภาษาที่สามารถใช้แทน C ได้
- แม้ปัจจุบันยังอยู่ระหว่างการพัฒนา แต่ก็ถูกใช้งานแล้วในโปรเจกต์อย่าง Bun และ TigerBeetle
- หนึ่งในความสามารถที่น่าประทับใจที่สุดของ Zig คือการทำงานร่วมกับ 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 แล้วใช้งานได้เหมือนกับการ import แบบปกติของ Zig
- โค้ดตัวอย่าง:
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 เพื่อไล่ดูฟิลด์และการประกาศของ struct ได้
- ด้วยวิธีนี้จึงสามารถสะท้อน 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 มาพร้อม toolchain ของ C และสามารถรวม declaration จากไฟล์เฮดเดอร์ C ได้อย่างราบรื่น
- ปรัชญาแบบ practical ของ Zig จะเห็นได้ชัดทันทีเมื่อเริ่มเรียนรู้ภาษา
- การออกแบบที่ใช้งานง่ายและสม่ำเสมอของ Zig ช่วยเพิ่มประสิทธิภาพในการทำงาน
สรุปโดย GN⁺
- Zig เป็นภาษาใหม่ที่เน้นการเขียนโปรแกรมระดับล่างและระบบ พร้อมความสามารถในการทำงานร่วมกับ C ได้อย่างยอดเยี่ยม
- Zig สามารถนำเข้าไฟล์เฮดเดอร์ C มาใช้งานได้ และยังสะท้อน C มาโครภายใน Zig ได้ด้วย
- ปรัชญาแบบ practical และการออกแบบที่เข้าใจง่ายของ Zig ช่วยอย่างมากต่อการเรียนรู้และการใช้งานภาษา
- Zig เปิดทางให้ย้ายโค้ดเบส C เดิมมาสู่ Zig ได้ จึงช่วยลดอุปสรรคในการนำภาษาไปใช้งาน
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ฟีเจอร์
@cImportมีกำหนดจะถูกถอดออกโค้ดตัวอย่าง:
โค้ดที่เทียบเท่ากันในภาษา D:
คอมไพเลอร์จัดการส่วนที่เหลือให้
มีคนที่อยากได้ไวยากรณ์พิเศษสำหรับการนำเข้าไฟล์ C แต่ความเรียบง่ายแบบนี้ดีกว่า
อยากชอบ Zig แต่กำลังเจอปัญหาบางอย่าง
zig initมีโค้ดที่ไม่จำเป็นเยอะเกินไปzig build-exe filename.zigพรีโปรเซสเซอร์ของ Clang ไม่ได้ถูกทำเป็นขั้นตอนแยกก่อนคอมไพล์
มีการเขียนบล็อกเกี่ยวกับวิธีทำงานคล้ายกันในภาษา D โดยใช้ ImportC
ดูเหมือนว่าแต่ละ enum จะเพิ่มอย่างน้อย UINT16_MAX*sizeof(intptr_t) ไบต์เข้าไปในไฟล์ executable
นิยามฟังก์ชันดูอ่านง่ายมาก
ชอบเว็บไซต์นี้