iso_c_binding

Kind interoperabel dan tipe modul intrinsik iso_c_binding untuk kesesuaian dengan C.

Daftar isi

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.

KonstantaTipe C yang cocok
c_intint
c_floatfloat
c_doubledouble
c_longlong
c_charchar
c_boolbool dari stdbool.h
c_size_tsize_t

Program berikut mencetak nilai kind pada lingkungan ini dan membandingkannya dengan kind dari modul intrinsik iso_fortran_env.

fortran cek-kind.f90
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
bash
# kompilasi program pemeriksa kind
gfortran cek-kind.f90 -o cek-kind
# jalankan program hasil kompilasi
./cek-kind
text Keluaran
 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.

fortran tipe-titik.f90
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
bash
# kompilasi program contoh tipe turunan
gfortran tipe-titik.f90 -o tipe-titik
# jalankan program hasil kompilasi
./tipe-titik
text Keluaran
 dibaca ulang lewat pointer:   1.5000000000000000       -2.2500000000000000     
 ukuran titik (byte):                   16

Baris 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_int dan c_double. Penulisan real(kind=8) dibiarkan hanya pada kode lama yang tata letaknya sudah terkunci.
  • Mengambil alamat variabel tanpa atribut target. Standar mensyaratkan argumen c_loc berupa variabel scalar dengan atribut target, sehingga deklarasi real(c_double) :: x harus diubah menjadi real(c_double), target :: x sebelum alamatnya diambil.
  • Meneruskan bagian array melalui c_loc. Argumen c_loc wajib scalar, sehingga bagian seperti a(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_binding menyediakan 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, dan c_sizeof menghitung ukurannya dalam byte.
  • Alamat ditangani tipe c_ptr, diambil dengan c_loc atas variabel scalar beratribut target, dan dihubungkan kembali dengan c_f_pointer.

Referensi#