Pustaka C yang telah dikompilasi menjadi pustaka bersama (shared library) dapat dibungkus menjadi fungsi Julia yang aman dipakai, lengkap dengan anotasi tipe, validasi argumen, dan pengujian otomatis. Topik ini mempraktikkan alurnya secara utuh, mulai dari berkas kode sumber, kompilasi dengan gcc, pemanggilan melalui ccall, hingga pengujian wrapper dengan pustaka standar Test. Hasil akhirnya adalah binding, yaitu lapisan penghubung menuju pustaka asing, yang teruji dan siap dipakai ulang.
Berkas pustaka C#
Contoh memakai pustaka kecil bernama geometri yang berisi tiga fungsi murni, yaitu perhitungan luas lingkaran, sisi miring segitiga siku-siku, dan konversi suhu. Seluruhnya hanya bergantung pada math.h, sehingga pustaka ini dapat dikompilasi di hampir semua sistem. Berkasnya tidak memuat fungsi main karena geometri adalah sumber pustaka, bukan program yang dieksekusi sendiri, dan fungsinya diuji melalui Julia setelah dikompilasi.
// tidak-dijalankan
// geometri.c: sumber pustaka C untuk contoh binding Julia
#include <math.h>
double luas_lingkaran(double r) {
return M_PI * r * r;
}
double hipotenusa(double a, double b) {
return hypot(a, b);
}
double fahrenheit(double celsius) {
return celsius * 9.0 / 5.0 + 32.0;
}Setiap fungsi menerima argumen double dan mengembalikan double, sehingga pemetaan tipenya seragam, yaitu Cdouble pada seluruh posisi. Kebiasaan berpegang pada tipe sederhana seperti ini mempermudah pembungkusan pertama sebelum pustaka berkembang menjadi lebih kompleks.
Kompilasi menjadi pustaka bersama#
Kompilasi dilakukan dengan gcc menggunakan opsi -shared untuk menghasilkan pustaka bersama dan -fPIC untuk kode posisi-independen (position-independent code), yaitu kode yang dapat dimuat pada alamat memori mana pun saat program berjalan. Kedua opsi ini lazim dipasangkan untuk pustaka yang dimuat dari bahasa lain.
# mengompilasi geometri.c menjadi pustaka bersama geometri.so
gcc -shared -fPIC -o geometri.so geometri.cPerintah yang berhasil tidak menampilkan apa pun, dan berkas geometri.so muncul di folder kerja. Ekstensi .so adalah konvensi pustaka bersama pada Linux, sedangkan macOS memakai .dylib dan Windows memakai .dll.
Pemuatan dan pemanggilan pertama#
Pustaka dimuat otomatis pada ccall pertama yang menyebutnya, dan pemanggilannya tidak memerlukan penyiapan apa pun selain jalur pustaka. Contoh berikut memanggil ketiga fungsi geometri dengan tipe argumen Cdouble yang sudah dikenal dari tabel pemetaan.
println(ccall((:hipotenusa, "./geometri.so"), Cdouble, (Cdouble, Cdouble), 3.0, 4.0))
println(ccall((:luas_lingkaran, "./geometri.so"), Cdouble, (Cdouble,), 2.0))
println(ccall((:fahrenheit, "./geometri.so"), Cdouble, (Cdouble,), 100.0))5.0
12.566370614359172
212.0Ketiga nilai sesuai perhitungan manual, yaitu sisi miring segitiga dengan sisi tegak 3 dan 4, luas lingkaran berjari-jari 2, serta 100 derajat Celsius yang menjadi 212 derajat Fahrenheit.
Membungkus dengan fungsi Julia yang aman#
Pemanggilan ccall mentah menyebarkan nama simbol dan tuple tipe ke setiap titik pemanggilan. Wrapper mengumpulkan detail tersebut di satu tempat, yaitu fungsi Julia yang menerima tipe Julia alami, memvalidasi argumennya, melakukan konversi yang diperlukan, lalu memanggil C. Validasi penting karena fungsi C pada umumnya tidak memeriksa argumennya, sehingga masukan tak sah yang lolos berhenti di dalam kode C tanpa pesan yang jelas. Berkas berikut memuat dua wrapper beserta pengujianannya.
using Test
"""
luas_lingkaran(r::Real) -> Float64
Menghitung luas lingkaran berjari-jari `r` melalui fungsi C `luas_lingkaran`.
Jari-jari negatif memicu error `ArgumentError`.
"""
function luas_lingkaran(r::Real)
r >= 0 || throw(ArgumentError("jari-jari tidak boleh negatif"))
return ccall((:luas_lingkaran, "./geometri.so"), Cdouble, (Cdouble,), Float64(r))
end
"""
hipotenusa(a::Real, b::Real) -> Float64
Menghitung sisi miring segitiga siku-siku dengan sisi tegak `a` dan `b`
melalui fungsi C `hipotenusa`.
"""
function hipotenusa(a::Real, b::Real)
a >= 0 && b >= 0 || throw(ArgumentError("panjang sisi tidak boleh negatif"))
return ccall((:hipotenusa, "./geometri.so"), Cdouble, (Cdouble, Cdouble), Float64(a), Float64(b))
end
hasil = redirect_stdout(devnull) do
@testset "Wrapper geometri" begin
@test luas_lingkaran(2.0) == Float64(pi) * 2.0^2
@test hipotenusa(3.0, 4.0) == hypot(3.0, 4.0)
@test_throws ArgumentError luas_lingkaran(-1.0)
end
end
println("ujian lulus: ", hasil.n_passed)Eksekusi berkas tersebut dilakukan setelah langkah kompilasi, karena wrapper membutuhkan geometri.so yang sudah ada di folder yang sama.
# menjalankan pengujian wrapper terhadap pustaka hasil kompilasi
julia wrapper.jlujian lulus: 3Pengujian membandingkan hasil C dengan perhitungan Julia murni yang setara, yaitu Float64(pi) * 2.0^2 dan hypot(3.0, 4.0), sehingga kesalahan konversi tipe langsung terlihat. Ujian @test_throws memastikan validasi argumen bekerja sebelum nilai diteruskan ke C. Ringkasan @testset sengaja dialihkan ke devnull karena memuat kolom waktu, dan yang dicetak hanyalah jumlah ujian yang lulus.
Simbol yang tidak ada dan pembersihan#
Kesalahan ejaan simbol baru terasa saat panggilan dijalankan, bukan saat pustaka dimuat. Error yang muncul menyebut simbol yang dicari dan pustakanya, sehingga penyebabnya terbaca dari pesan tersebut tanpa alat tambahan.
try
ccall((:volume_bola, "./geometri.so"), Cdouble, (Cdouble,), 1.0)
catch e
println("error tertangkap: ", e.msg)
end
rm("geometri.so")
println("berkas geometri.so sudah dihapus: ", !isfile("geometri.so"))error tertangkap: could not load symbol "volume_bola":
./geometri.so: undefined symbol: volume_bola
berkas geometri.so sudah dihapus: truePesan lengkapnya aman ditampilkan karena jalur yang disebut bersifat relatif. Blok ini sekaligus menghapus hasil kompilasi agar folder contoh kembali hanya memuat kode sumber, sedangkan pada proyek sungguhan pustaka hasil kompilasi justru dipertahankan dan dimuat sekali di awal sesi.
Distribusi melalui JLL#
Wrapper buatan sendiri bergantung pada pustaka hasil kompilasi yang harus tersedia di mesin setiap pengguna, lengkap dengan perbedaan antarsistem operasi. Ekosistem Julia menyelesaikan masalah tersebut melalui package JLL, yaitu package yang hanya berisi pustaka biner hasil build silang untuk banyak platform. Pustaka biner itu dibangun oleh proyek BinaryBuilder dari resep pada repositori Yggdrasil. Package yang membutuhkan pustaka C cukup menjadikan JLL terkait sebagai dependensinya, kemudian memanggil pustakanya melalui ccall dengan jalur yang diberikan oleh artefak tersebut. Pola inilah yang dipakai banyak package ilmiah, sehingga pengguna tidak perlu memasang compiler ataupun pustaka sistem apa pun.
Kesalahan umum#
Kesalahan pertama adalah memanggil pustaka dengan nama tanpa garis miring, misalnya "geometri.so", yang berujung pada pesan could not load library karena pencariannya diarahkan ke jalur pustaka sistem. Perbaikannya adalah menulis ./geometri.so selama folder kerja masih sama dengan letak pustakanya.
Kesalahan kedua adalah menyusun tuple tipe yang tidak sesuai. Konversi yang tidak sah, misalnya nilai pecahan pada posisi Cint, melempar InexactError, sedangkan ketidakcocokan pada tipe pointer dapat berjalan tanpa pesan tetapi merusak memori. Tuple tipe sebaiknya disalin langsung dari dokumentasi fungsi C, kemudian divalidasi melalui pengujian seperti pada bagian wrapper.
Kesalahan ketiga adalah menyunting kode C tanpa mengompilasi ulang, sehingga geometri.so versi lama tetap dimuat dan perubahan tampak tidak berdampak. Kompilasi ulang dilakukan setiap kali kode sumber berubah, dan pustaka yang diperbarui terbaca pada sesi Julia yang baru.
Ringkasan#
- Alur pembungkusan terdiri atas berkas
.c, kompilasigcc -shared -fPIC, lalu pemanggilanccalldengan jalur pustaka. - Wrapper mengumpulkan nama simbol dan tuple tipe di satu fungsi Julia yang memvalidasi argumen dan mengonversi tipe secara eksplisit.
- Pengujian membandingkan hasil C dengan perhitungan Julia murni yang setara, termasuk ujian
@test_throwsatas validasi argumennya. - Distribusi pustaka biner di ekosistem Julia memakai package JLL hasil build silang BinaryBuilder, sehingga pengguna tidak perlu mengompilasi sendiri.
Referensi#
- Calling C and Fortran Code — Julia Documentation
— Sintaks
ccalldan pemetaan tipe yang menjadi dasar wrapper. - Shared Library Options — GCC
— Opsi
-shareddan-fPICyang dipakai pada kompilasi pustaka bersama. - BinaryBuilder.jl — JuliaPackaging — Alat resmi pembangun artefak biner lintas platform untuk package JLL.