lib dan fun

Mendeklarasikan fungsi asing dengan lib, fun, dan atribut @[Link].

Daftar isi

Crystal dapat memanggil fungsi yang ditulis dalam bahasa C secara langsung, tanpa proses terpisah dan tanpa antarmuka jaringan. Mekanisme ini disebut binding, yaitu pengikatan simbol pustaka C ke dalam program Crystal sehingga fungsi tersebut dapat dipanggil sebagaimana metode module biasa. Topik ini membahas blok lib yang mengelompokkan deklarasi, kata kunci fun yang mendeklarasikan satu fungsi C, atribut @[Link] yang menautkan pustakanya, serta pemetaan tipe data di antara kedua bahasa.

Blok lib dan deklarasi fun#

Deklarasi fungsi C ditulis di dalam blok lib. Blok ini berperan sebagai namespace tersendiri, yaitu ruang nama yang mengelompokkan simbol C agar tidak bertabrakan dengan pengenal Crystal. Setiap fungsi dideklarasikan dengan kata kunci fun yang wajib mencantumkan tipe setiap argumen dan tipe nilai baliknya, karena compiler Crystal tidak membaca berkas C untuk mengetahuinya. Program berikut mendeklarasikan dua fungsi pustaka matematika C, yaitu sqrt dan pow, lalu memanggil keduanya.

crystal matematika.cr
@[Link("m")]
lib Matematika
  fun sqrt(x : Float64) : Float64
  fun pow(x : Float64, y : Float64) : Float64
end

puts Matematika.sqrt(144.0)
puts Matematika.pow(2.0, 10.0)
bash
# jalankan program yang memanggil pustaka matematika C
crystal run matematika.cr
text Keluaran
12.0
1024.0

Pemanggilan Matematika.sqrt(144.0) terlihat seperti pemanggilan metode module, tetapi yang dijalankan adalah kode mesin dari pustaka C. Kedua fungsi menggunakan tipe double pada deklarasi C-nya, sehingga kedua sisinya ditulis Float64. Kesesuaian tipe inilah kontrak utama binding: compiler memercayai deklarasi yang ditulis dan tidak memverifikasinya terhadap berkas header C.

Deklarasi binding biasanya dikumpulkan dalam satu blok lib untuk setiap pustaka agar mudah dicari dan dirawat. Pustaka standar Crystal menggunakan pola yang sama, yaitu mengumpulkan deklarasi C yang sudah siap pakai di bawah module LibC, sehingga tipe seperti LibC::SizeT dan fungsi seperti LibC.free dapat digunakan tanpa deklarasi tambahan. Kebiasaan mengelompokkan ini juga menjaga nama panggilan tetap konsisten di seluruh program, tanpa perbedaan gaya penamaan antarberkas sumber.

Deklarasi fun hanya memberi tahu compiler nama dan tipe sebuah fungsi. Penyambungan kode mesinnya dilakukan belakangan pada tahap link, yaitu ketika pustaka yang memuat fungsi tersebut diikutkan ke dalam program akhir. Pustaka matematika C terpisah dari libc dan perlu ditautkan secara eksplisit dengan atribut @[Link] yang diletakkan di atas blok lib. Atribut @[Link("m")] menghasilkan opsi -lm pada perintah link, sehingga simbol sqrt dan pow dicari sebagai libm di jalur pustaka bawaan tanpa perlu jalur berkas apa pun. Satu blok lib juga boleh membawa lebih dari satu atribut @[Link] apabila pustakanya bergantung pada beberapa pustaka lain sekaligus, dan setiap atribut menjadi satu opsi tersendiri.

Penempatan atribut tidak boleh salah. Atribut @[Link] melekat pada blok lib, sedangkan fun hanya menerima atribut yang berkaitan dengan pembangkitan kode. Program berikut menaruh @[Link] di atas fun dan ditolak compiler.

crystal lib_fun_rusak.cr
lib Matematika
  @[Link("m")]
  fun sqrt(x : Float64) : Float64
end

puts Matematika.sqrt(144.0)
bash
# compiler menolak atribut yang salah tempat
crystal run lib_fun_rusak.cr
text Keluaran
Showing last frame. Use --error-trace for full trace.

In lib_fun_rusak.cr:2:3

 2 | @[Link("m")]
     ^
Error: funs can only be annotated with: NoInline, AlwaysInline, Naked, ReturnsTwice, Raises, CallConvention

Pesan error memuat daftar atribut yang sah untuk fun, dan @[Link] bukan salah satunya. Perbaikannya adalah memindahkan @[Link("m")] satu baris ke atas sehingga menempel pada blok lib, seperti pada matematika.cr.

Pemetaan tipe dan penamaan ulang#

Tipe pada deklarasi fun memetakan tipe C ke tipe Crystal dengan ukuran yang sama. Tabel berikut memuat pemetaan yang lazim diperlukan.

Tipe CTipe Crystal
intInt32
longInt64
floatFloat32
doubleFloat64
char*Pointer(LibC::Char)
void*Void*
size_tLibC::SizeT

Nama fungsi C juga dapat diganti saat dideklarasikan. Bentuk fun nama_baru = nama_asli mendeklarasikan simbol C nama_asli dengan nama panggilan nama_baru di sisi Crystal. Cara ini berguna apabila nama aslinya bertabrakan dengan pengenal Crystal atau tidak sesuai dengan konvensi penamaan program.

crystal alias_libc.cr
lib Nomor
  fun nilai_mutlak = abs(x : Int32) : Int32
end

puts Nomor.nilai_mutlak(-7)
puts Nomor.nilai_mutlak(42)
bash
# fungsi abs dari libc dipanggil dengan nama baru
crystal run alias_libc.cr
text Keluaran
7
42

Fungsi abs dari libc kini hanya dikenal sebagai Nomor.nilai_mutlak, sedangkan simbol C-nya tetap abs. Penamaan ulang tidak mengubah kode yang dijalankan, hanya nama yang digunakan pada pemanggilan.

Kesalahan umum#

Kesalahan pertama adalah mendeklarasikan tipe yang tidak sesuai dengan deklarasi C, misalnya Int32 padahal C-nya long. Pemanggilan berlangsung pada tingkat mesin tanpa pemeriksaan ulang, karena argumen dipindahkan sebagai byte mentah menurut konvensi pemanggilan C. Akibatnya program tetap terkompilasi, tetapi nilainya terbaca kacau atau programnya berhenti secara tak terduga. Perbaikannya adalah mencocokkan deklarasi dengan berkas header C dan memanfaatkan sizeof untuk memastikan ukuran tiap tipe.

Kesalahan kedua adalah melupakan @[Link] untuk pustaka yang tidak ditautkan secara default. Error baru muncul pada tahap link, bukan saat kompilasi Crystal, dengan pesan undefined reference yang menyebut nama simbol yang tidak ditemukan. Nama pada pesan itu langsung menunjuk deklarasi yang pustakanya belum diikutkan. Perbaikannya adalah menambahkan atribut @[Link] yang sesuai di atas blok lib, atau memastikan pustakanya benar-benar terpasang di sistem sebelum kompilasi diulang.

Ringkasan#

  • Fungsi C dideklarasikan dengan fun di dalam blok lib, lengkap dengan tipe argumen dan tipe nilai balik, lalu dipanggil seperti metode module.
  • Atribut @[Link] menautkan pustaka pada tahap link dan diletakkan di atas blok lib, bukan di atas fun.
  • Tipe pada fun memetakan tipe C ke tipe Crystal yang berukuran sama, dan kesesuaiannya tidak diperiksa ulang saat pemanggilan.
  • Bentuk fun nama_baru = nama_asli memanggil simbol C dengan nama panggilan bergaya Crystal.

Referensi#