1 poin oleh GN⁺ 2024-07-31 | 1 komentar | Bagikan ke WhatsApp

Refleksi Makro C di Zig

  • Zig

    • Zig adalah bahasa pemrograman baru yang berfokus pada pemrograman tingkat rendah dan sistem, serta sedang memantapkan posisinya sebagai bahasa yang dapat menggantikan C
    • Saat ini masih dalam pengembangan, tetapi sudah digunakan oleh proyek seperti Bun dan TigerBeetle
    • Salah satu fitur Zig yang paling mengesankan adalah interoperabilitasnya yang sangat baik dengan C
  • Memanggil pustaka eksternal

    • Di Zig, pustaka eksternal dapat dipanggil dengan mudah
    • Contoh kode:
      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);
      }
      
  • Mengimpor file header C

    • Di Zig, file header C dapat diimpor dan digunakan seperti impor Zig biasa
    • Contoh kode:
      const win32 = @cImport({
        @cInclude("windows.h");
        @cInclude("winuser.h");
      });
      pub fn main() !void {
        _ = win32.MessageBoxA(null, "world!", "Hello", 0);
      }
      
  • Pemrograman Windows

    • Aplikasi Windows pada umumnya memiliki fungsi main dan fungsi window procedure
    • Fungsi main menginisialisasi aplikasi dan menjalankan loop yang meneruskan pesan ke window procedure
    • Window procedure menerima dan menangani pesan
    • Contoh kode:
      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;
      }
      
  • Refleksi

    • Memetakan makro C bisa menjadi pekerjaan yang merepotkan
    • Di Zig, fungsi @typeInfo dapat digunakan untuk menampilkan field dan deklarasi struct
    • Dengan ini, makro C dapat direfleksikan di Zig
    • Contoh kode:
      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);
      }
      
  • Kesimpulan

    • Zig memungkinkan fungsi-fungsi C dilakukan dengan lebih nyaman menggunakan struktur bahasa pemrograman yang lebih modern
    • Zig menyertakan toolchain kompiler C sehingga dapat memasukkan deklarasi dari file header C dengan mulus
    • Filosofi pragmatis Zig langsung terasa begitu mulai mempelajari bahasanya
    • Desain Zig yang intuitif dan konsisten membantu meningkatkan produktivitas

Ringkasan GN⁺

  • Zig adalah bahasa baru yang berfokus pada pemrograman tingkat rendah dan sistem, dengan interoperabilitas yang sangat baik dengan C
  • Zig dapat mengimpor dan menggunakan file header C, serta merefleksikan makro C di dalam Zig
  • Filosofi pragmatis dan desain Zig yang intuitif sangat membantu dalam mempelajari dan menggunakan bahasa ini
  • Zig menyediakan jalur untuk memindahkan codebase C yang sudah ada ke Zig, sehingga mengatasi hambatan adopsi bahasa

1 komentar

 
GN⁺ 2024-07-31
Komentar Hacker News
  • Fitur @cImport akan dihapus

    • Mengimpor file C tetap dimungkinkan, tetapi memerlukan lebih banyak pekerjaan
    • Fitur ini ingin dihapus dari bahasa untuk menghilangkan ketergantungan pada libclang
  • Contoh kode:

    const win32 = @cImport({
      @cInclude("windows.h");
      @cInclude("winuser.h");
    });
    
    pub fn main() !void {
      _ = win32.MessageBoxA(null, "world!", "Hello", 0);
    }
    
  • Kode yang setara dalam bahasa D:

    import windows, winuser;
    void main() {
      MessageBoxA(null, "world!", "Hello", 0);
    }
    
  • Kompiler menangani sisanya

  • Ada orang yang meminta sintaks khusus untuk mengimpor file C, tetapi kesederhanaan ini lebih baik

  • Saya ingin menyukai Zig, tetapi mengalami beberapa masalah

    • Sebagian besar mungkin karena ini masih belum versi 1.0
    • Misalnya, cara yang direkomendasikan untuk memulai proyek dengan zig init memiliki banyak kode yang tidak perlu
    • Baru-baru ini saya mengetahui bahwa bagian inisialisasi bisa dilewati dengan zig build-exe filename.zig
    • Ada juga banyak masalah integrasi editor
    • Saya memasang ekstensi VSCode, tetapi autocomplete dan sebagainya tidak berfungsi dengan baik
    • Kemungkinan besar ini kesalahan pengguna, jadi saya berencana mencobanya lagi akhir pekan ini
  • Preprosesor Clang tidak diimplementasikan sebagai tahap pra-kompilasi yang terpisah

    • Pada dasarnya ini adalah bagian dari lexer
    • Saya rasa gcc juga menggunakan pendekatan yang serupa
    • Mengakses nama makro secara teknis bukanlah hal yang mustahil
    • Ini tidak diimplementasikan karena permintaannya tidak terlalu banyak
  • Saya menulis di blog tentang cara melakukan pekerjaan serupa di bahasa D menggunakan ImportC

  • Sepertinya setiap enum akan menambahkan setidaknya UINT16_MAX*sizeof(intptr_t) byte ke file executable

  • Definisi fungsinya terlihat sangat mudah dibaca

    • Saya pernah melihatnya di bahasa lain, tetapi biasanya sangat mengerikan
    • Mungkin Zig layak dipelajari
    • Ini adalah fitur killer
  • Saya suka situsnya

    • Zig tampaknya benar-benar sedang mendapatkan popularitas