Modul intrinsik iso_c_binding menyediakan konstanta kind dan prosedur bawaan yang membuat deklarasi Fortran sesuai dengan tipe serta konvensi C. Topik ini membahas konstanta kind interoperabel yang paling sering dipakai, deklarasi tipe turunan yang tata letak memorinya mengikuti aturan struct C, dan penanganan alamat memori melalui tipe c_ptr. Dua program pemeriksa disertakan, dikompilasi, lalu dijalankan, sehingga kesesuaian tipe pada lingkungan kompilasi ini dapat diamati langsung dari keluarannya. Pemahaman atas ketiga hal tersebut menjadi dasar bagi pemanggilan fungsi C dan pustaka numerik yang kontrak antargayanya sudah disepakati kedua bahasa.
Kind sebagai kontrak tipe dengan C#
Kind menentukan cara sebuah nilai disimpan di memori, tetapi standar Fortran tidak menetapkan angka kind-nya, sehingga real(kind=8) yang berarti double precision pada satu kompiler dapat bermakna lain pada kompiler yang berbeda. Modul intrinsik iso_c_binding, yang tersedia sejak standar Fortran 2003, menyelesaikan ketidakpastian itu dengan konstanta bernama yang nilainya dipilih kompiler agar cocok dengan tipe C yang bersangkutan. Program yang mendeklarasikan real(c_double) dijamin sesuai dengan double pada C tanpa bergantung pada angka kind tertentu.
Tabel berikut memuat konstanta yang paling sering dipakai pada kode interoperabel.
| Konstanta | Tipe C yang cocok |
|---|---|
c_int | int |
c_float | float |
c_double | double |
c_long | long |
c_char | char |
c_bool | bool dari stdbool.h |
c_size_t | size_t |
Program berikut mencetak nilai kind pada lingkungan ini dan membandingkannya dengan kind dari modul intrinsik iso_fortran_env.
program cek_kind
use, intrinsic :: iso_c_binding, only: c_int, c_double, c_float, c_char, c_bool, c_long
use, intrinsic :: iso_fortran_env, only: int32, real32, real64
implicit none
print *, "kind c_int =", c_int, "(sama dengan int32 :", c_int == int32, ")"
print *, "kind c_float =", c_float, "(sama dengan real32:", c_float == real32, ")"
print *, "kind c_double =", c_double, "(sama dengan real64:", c_double == real64, ")"
print *, "kind c_char =", c_char
print *, "kind c_bool =", c_bool
print *, "kind c_long =", c_long
print *, "literal 1.0_c_double:", 1.0_c_double
end program cek_kind# kompilasi program pemeriksa kind
gfortran cek-kind.f90 -o cek-kind
# jalankan program hasil kompilasi
./cek-kind kind c_int = 4 (sama dengan int32 : T )
kind c_float = 4 (sama dengan real32: T )
kind c_double = 8 (sama dengan real64: T )
kind c_char = 1
kind c_bool = 1
kind c_long = 8
literal 1.0_c_double: 1.0000000000000000 Pada gfortran 14.2 untuk arsitektur x86-64, c_int bernilai 4 dan sama dengan int32, sedangkan c_double bernilai 8 dan sama dengan real64. Angka-angka tersebut bukan bagian dari standar dan boleh berbeda pada kompiler lain. Bagian yang dijamin standar adalah kecocokan setiap konstanta dengan tipe C-nya, sehingga kebetulan lingkungan ini yang mencerminkan kebiasaan umum kompiler modern tidak boleh diperlakukan sebagai kontrak.
Tipe turunan yang interoperabel dengan struct C#
Deklarasi type, bind(c) menghasilkan tipe turunan yang tata letak memorinya mengikuti aturan struct C. Setiap komponen wajib bertipe interoperabel, yaitu bertipe intrinsik dengan kind dari iso_c_binding, dan tipe turunan tersebut tidak boleh memiliki prosedur terkait tipe (type-bound procedure). Dengan dua syarat itu, objek Fortran dan objek C dapat saling dipertukarkan tanpa konversi, misalnya ketika struct dialokasikan oleh kode C lalu dibaca oleh Fortran melalui alamat yang sama.
Program berikut mendeklarasikan tipe titik dengan dua komponen real(c_double), mengambil alamat objeknya dengan c_loc, lalu membaca ulang isinya melalui pointer Fortran yang dihubungkan kembali dengan c_f_pointer.
program tipe_titik
use, intrinsic :: iso_c_binding
implicit none
type, bind(c) :: titik
real(c_double) :: x
real(c_double) :: y
end type titik
type(titik), target :: p
type(titik), pointer :: q
type(c_ptr) :: alamat
p%x = 1.5_c_double
p%y = -2.25_c_double
alamat = c_loc(p)
call c_f_pointer(alamat, q)
print *, "dibaca ulang lewat pointer:", q%x, q%y
print *, "ukuran titik (byte):", c_sizeof(p)
end program tipe_titik# kompilasi program contoh tipe turunan
gfortran tipe-titik.f90 -o tipe-titik
# jalankan program hasil kompilasi
./tipe-titik dibaca ulang lewat pointer: 1.5000000000000000 -2.2500000000000000
ukuran titik (byte): 16Baris pertama keluaran menunjukkan q membaca nilai yang sama dengan yang ditugaskan pada p, karena keduanya menunjuk objek yang sama di memori. Baris kedua menunjukkan c_sizeof(p) menghasilkan 16 byte, tepat dua kali ukuran double, sehingga tidak ada byte pengisi (padding) pada tipe sederhana ini. Untuk tipe yang komponennya beragam ukurannya, aturan penyelarasan struct C tetap berlaku, sehingga urutan deklarasi komponen memengaruhi ukuran akhirnya.
Alamat memori dan tipe c_ptr#
Tipe c_ptr adalah wadah alamat yang dipahami oleh kedua bahasa, dan objeknya tidak boleh dihitung secara aritmetika seperti pointer C. Alamat sebuah variabel diperoleh dengan prosedur c_loc, yang argumennya wajib berupa variabel scalar yang memiliki atribut target. Arah sebaliknya dilakukan dengan c_f_pointer, yaitu menghubungkan sebuah c_ptr dengan pointer Fortran sehingga nilainya dapat diakses dengan sintaks Fortran biasa. Untuk pointer prosedur, pasangannya adalah c_funloc dan c_f_procpointer, yang dipakai pada pemanggilan balik di topik tentang prosedur bind(c) di modul ini.
Apabila alamat perlu diperlakukan sebagai bilangan bulat, misalnya untuk menghitung selisih antara dua alamat, c_ptr dikonversi dengan transfer ke tipe integer(c_intptr_t). Tipe tersebut cocok dengan intptr_t pada C dan menjamin cukup menampung alamat pada arsitektur yang bersangkutan. Pola ini dipakai pada topik terakhir modul ini untuk membuktikan urutan penyimpanan elemen array melalui selisih alamatnya.
Kesalahan umum#
- Menganggap angka kind sebagai kontrak. Nilai 4 dan 8 pada keluaran di atas berlaku untuk gfortran pada x86-64 saja, sedangkan kode yang portabel memakai
c_intdanc_double. Penulisanreal(kind=8)dibiarkan hanya pada kode lama yang tata letaknya sudah terkunci. - Mengambil alamat variabel tanpa atribut target. Standar mensyaratkan argumen
c_locberupa variabel scalar dengan atributtarget, sehingga deklarasireal(c_double) :: xharus diubah menjadireal(c_double), target :: xsebelum alamatnya diambil. - Meneruskan bagian array melalui
c_loc. Argumenc_locwajib scalar, sehingga bagian sepertia(1:2)tidak diteruskan dengan cara itu. Bagian array dikirim sebagai argumen array prosedur interoperabel, sebagaimana dipakai pada topik tentang array lintas bahasa di modul ini.
Ringkasan#
iso_c_bindingmenyediakan konstanta kind yang dijamin cocok dengan tipe C, sehingga deklarasi tipe tidak lagi bergantung pada angka kind kompiler.- Tipe turunan dengan
bind(c)menuruti aturan tata letak struct C, danc_sizeofmenghitung ukurannya dalam byte. - Alamat ditangani tipe
c_ptr, diambil denganc_locatas variabel scalar beratributtarget, dan dihubungkan kembali denganc_f_pointer.
Referensi#
- Learn: tutorial resmi Fortran — fortran-lang — Kumpulan tutorial resmi komunitas Fortran yang memuat materi interoperabilitas dengan C.
- The GNU Fortran Compiler — GNU Project — Manual gfortran yang menerangkan modul iso_c_binding dan aturan interoperabilitas pada kompiler yang dipakai jalur ini.