1 puan yazan GN⁺ 2024-07-31 | 1 yorum | WhatsApp'ta paylaş

Zig'de C makro yansıtması

  • Zig

    • Zig, düşük seviye ve sistem programlamaya odaklanan yeni bir programlama dilidir ve C'nin yerini alabilecek bir dil olarak konumlanmaktadır
    • Hâlâ geliştirme aşamasında olsa da Bun ve TigerBeetle gibi projelerde şimdiden kullanılmaktadır
    • Zig'in en etkileyici özelliklerinden biri C ile mükemmel birlikte çalışabilirliğidir
  • Harici kütüphaneleri çağırma

    • Zig'de harici kütüphaneleri çağırmak kolaydır
    • Örnek kod:
      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 header dosyalarını içe aktarma

    • Zig'de C header dosyaları içe aktarılıp normal bir Zig import'u gibi kullanılabilir
    • Örnek kod:
      const win32 = @cImport({
        @cInclude("windows.h");
        @cInclude("winuser.h");
      });
      pub fn main() !void {
        _ = win32.MessageBoxA(null, "world!", "Hello", 0);
      }
      
  • Windows programlama

    • Tipik bir Windows uygulamasında bir main fonksiyonu ve bir window procedure fonksiyonu bulunur
    • main fonksiyonu uygulamayı başlatır ve mesajları window procedure'a ileten döngüyü çalıştırır
    • window procedure gelen mesajları alıp işler
    • Örnek kod:
      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;
      }
      
  • Yansıtma

    • C makrolarını eşlemek zahmetli olabilir
    • Zig'de @typeInfo fonksiyonu kullanılarak struct alanları ve bildirimleri listelenebilir
    • Bu sayede C makroları Zig içinde yansıtılabilir
    • Örnek kod:
      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);
      }
      
  • Sonuç

    • Zig, C'nin yapabildiği işleri daha modern programlama dili yapılarıyla daha kullanışlı biçimde gerçekleştirebilir
    • Zig, C compiler toolchain'i de içererek C header dosyalarındaki bildirimleri sorunsuz biçimde dahil edebilir
    • Zig'in pragmatist felsefesi, dili öğrenmeye başlar başlamaz kendini gösterir
    • Zig'in sezgisel ve tutarlı tasarımı üretkenliği artırmaya katkı sağlar

GN⁺ özeti

  • Zig, düşük seviye ve sistem programlamaya odaklanan yeni bir dildir ve C ile mükemmel birlikte çalışabilirlik sunar
  • Zig, C header dosyalarını içe aktararak kullanabilir ve C makrolarını Zig içinde yansıtabilir
  • Zig'in pragmatist felsefesi ve sezgisel tasarımı, dili öğrenmeyi ve kullanmayı büyük ölçüde kolaylaştırır
  • Zig, mevcut C kod tabanlarını Zig'e taşıyacak bir yol sunarak dilin benimsenmesinin önündeki engelleri aşar

1 yorum

 
GN⁺ 2024-07-31
Hacker News görüşleri
  • @cImport özelliğinin kaldırılması planlanıyor

    • C dosyalarını içe aktarmak mümkün, ancak daha fazla işlem gerekiyor
    • libclang bağımlılığını kaldırmak için bu özelliği dilden çıkarmak istiyorlar
  • Örnek kod:

    const win32 = @cImport({
      @cInclude("windows.h");
      @cInclude("winuser.h");
    });
    
    pub fn main() !void {
      _ = win32.MessageBoxA(null, "world!", "Hello", 0);
    }
    
  • D dilindeki eşdeğer kod:

    import windows, winuser;
    void main() {
      MessageBoxA(null, "world!", "Hello", 0);
    }
    
  • Geri kalanını derleyici hallediyor

  • C dosyalarını içe aktarmak için özel bir sözdizimi isteyenler var, ancak bu sadelik daha iyi

  • Zig’i sevmek istiyorum ama birkaç sorun yaşıyorum

    • Bunun çoğunun hâlâ 1.0 sürümüne ulaşmamış olmasından kaynaklandığını düşünüyorum
    • Örneğin, zig init ile proje başlatmanın önerilen yolunda gereksiz çok fazla kod var
    • Yakın zamanda zig build-exe filename.zig ile başlatma kısmını atlayabildiğinizi öğrendim
    • Editör entegrasyonuyla ilgili de birçok sorun yaşadım
    • VSCode eklentisini kurdum ama otomatik tamamlama gibi şeyler düzgün çalışmıyor
    • Muhtemelen kullanıcı hatasıdır; hafta sonu tekrar denemeyi planlıyorum
  • Clang’ın önişlemcisi ayrı bir derleme öncesi aşama olarak uygulanmıyor

    • Esasen lexer’ın bir parçası
    • gcc’nin de benzer bir yaklaşım kullandığını düşünüyorum
    • Makro isimlerine erişmek teknik olarak imkânsız değil
    • Yeterli talep olmadığı için uygulanmıyor
  • D dilinde ImportC kullanarak benzer bir işi nasıl yapabileceğinize dair bir blog yazdım

  • Her enum için çalıştırılabilir dosyaya en az UINT16_MAX*sizeof(intptr_t) bayt eklenecek gibi görünüyor

  • Fonksiyon tanımları çok okunaklı görünüyor

    • Bunu başka dillerde de gördüm ama genelde oldukça korkunç oluyor
    • Belki de Zig öğrenmeye değer
    • Bu tam bir killer feature
  • Siteyi beğendim

    • Zig gerçekten popülerlik kazanıyor gibi görünüyor