Ekspor ke C

Menyediakan fungsi Zig untuk dipanggil dari program C melalui export dan tautan objek.

Daftar isi

Arah interoperasi yang berlawanan dari penerjemahan header adalah membuat kode Zig dapat dipakai oleh program C. Zig mendukung arah ini tanpa alat bantu tambahan, karena kata kunci export menempatkan fungsi pada tabel simbol dengan konvensi C dan tipe dasarnya dijamin cocok dengan ABI C. Topik ini membangun berkas objek dari fungsi Zig, menautkannya dengan program C, dan menjalankan hasil gabungannya.

Konsep#

Kata kunci export pada deklarasi fungsi menandakan dua hal. Fungsi tersebut memakai konvensi pemanggilan C, dan simbolnya dipancarkan pada berkas hasil sehingga penaut bahasa lain dapat menemukannya. Tanpa export, fungsi Zig dapat dihilangkan atau diubah bentuknya oleh kompilator karena kode Zig yang menggunakannya sudah tersatukan (inlined) pada waktu kompilasi.

Tipe parameter dan nilai kembalian wajib memiliki tata letak yang dikenal C. Tipe numerik berlebar tetap seperti i32 dan f64, pointer, serta extern struct memenuhi syarat itu, sedangkan tipe Zig murni seperti []u8 atau optional tidak memiliki padanan C dan tidak boleh melintasi batas tersebut. Aturan yang sama berlaku pada extern struct yang dibahas pada topik berikutnya, sehingga dua topik ini saling melengkapi.

Gaya pelaporan kegagalan juga mengikuti sisi C, karena error union Zig tidak dapat dikembalikan melintasi batas ABI. Fungsi yang dapat gagal mengembalikan kode bilangan bulat, misalnya nol untuk berhasil dan nilai bukan nol untuk jenis kegagalan, atau mengembalikan nilai lewat parameter pointer keluaran yang diberikan pemanggil C. Kesepakatan itu dituliskan pada dokumentasi fungsi Zig-nya, dan konsumen C memperlakukannya sebagaimana konvensi pustaka C pada umumnya.

Alur build untuk arah ini berurutan sebagai berikut.

  1. Tulis fungsi Zig dengan export.
  2. Kompilasi menjadi berkas objek dengan zig build-obj.
  3. Tulis program C yang mendeklarasikan fungsinya dengan extern.
  4. Tautkan program C dengan berkas objek tersebut, lalu jalankan.

Contoh Lengkap#

Berkas Zig menyediakan dua fungsi aritmetika sederhana yang diekspor.

zig aritmetika.zig
// tidak-dijalankan: berkas ekspor dikompilasi lewat perintah zig build-obj
export fn tambah(a: i32, b: i32) i32 {
    return a + b;
}

export fn kali(a: i32, b: i32) i32 {
    return a * b;
}

Tanda tangan keduanya memakai i32 dan nilai kembalian i32, padanan langsung int pada C. Kompilasi menjadi berkas objek, kemudian program C memanggilnya.

c pemanggil.c
// tidak-dijalankan: berkas C ditautkan bersama objek Zig lewat perintah zig cc
#include <stdio.h>

extern int tambah(int a, int b);
extern int kali(int a, int b);

int main(void) {
    printf("tambah 19 + 23 = %d\n", tambah(19, 23));
    printf("kali 6 * 7 = %d\n", kali(6, 7));
    return 0;
}

Deklarasi extern int tambah(int, int) pada sisi C adalah janji kecocokan tanda tangan, dan pembacaan berulang pada kedua sisi diperlukan karena tidak ada pemeriksaan otomatis antar bahasa. Build berlangsung dua langkah, yaitu menghasilkan objek lalu menautkannya dengan zig cc.

bash
zig build-obj aritmetika.zig -femit-bin=aritmetika.o
zig cc pemanggil.c aritmetika.o -o pemanggil
./pemanggil
text Keluaran
tambah 19 + 23 = 42
kali 6 * 7 = 42

Kedua baris keluaran dihitung oleh fungsi Zig di dalam program C, tanpa pustaka penghubung, tanpa header generator, dan tanpa runtime tambahan. Perintah zig cc berperan sebagai penaut biasa yang menerima berkas objek Zig sebagaimana menerima berkas objek dari kompilator C mana pun.

Lapisan ekspor sebaiknya dijaga tetap tipis. Fungsi yang diekspor menerima dan mengembalikan tipe C, lalu mendelegasikan seluruh logika ke fungsi Zig biasa di berkas yang sama, dan pola ini menjaga logika tetap dapat diuji dengan blok test tanpa proses C. Simbol yang dipancarkan juga dapat diperiksa dengan nm atas berkas objeknya, dan nama fungsi tambah serta kali hadir tanpa pengubah tambahan karena Zig tidak merusak nama fungsi yang diekspor.

Konvensi pemanggilan dan penamaan simbol menjadi kontrak yang dipelihara kedua sisi. Zig menuliskan nama fungsi yang diekspor apa adanya, sehingga deklarasi extern pada sisi C hanya perlu cocok pada tanda tangan, bukan pada pengubah nama. Sebaliknya, setiap perubahan tanda tangan di sisi Zig menuntut pembaruan deklarasi di sisi C, dan pemeriksaan nm atas berkas objek menjadi cara cepat memastikan simbol yang dijanjikan benar-benar hadir.

Kesalahan umum#

Kesalahan kedua berupa lupa menulis export, sehingga fungsi hanya terlihat di dalam Zig. Gejalanya berupa undefined reference to 'tambah' saat penautan, dan perbaikannya berupa menambahkan kata kunci export pada kedua fungsi. Kesalahan serupa terjadi bila fungsi ditandai export tetapi tidak diemitsikan karena kompilasi memakai mode yang memangkas simbol, dan opsi -fno-strip atau mode debug menghindarinya.

Kesalahan ketiga berupa mengirim tipe Zig murni melintasi batas. Parameter []const u8 pada fungsi export tampak wajar bagi penulis Zig, tetapi penaut C tidak mengetahui tipe tersebut karena slice membawa pointer dan panjang sekaligus. Pola yang benar adalah mengirim pointer mentah [*c]const u8 beserta panjangnya sebagai parameter usize yang terpisah.

Ringkasan#

  • export menempatkan fungsi Zig pada simbol objek dengan konvensi pemanggilan C.
  • Hanya tipe ber-ABI C, yaitu numerik berlebar tetap, pointer, dan extern struct, yang boleh melintasi batas.
  • Alur build-nya dua langkah, yaitu zig build-obj lalu penautan dengan zig cc atau penaut C lainnya.
  • Kecocokan tanda tangan antar bahasa adalah tanggung jawab penulis, karena tidak ada pemeriksaan otomatis.

Referensi#