Program Ada jarang berdiri sendiri. Sebagian besar lingkungan target menyediakan pustaka yang ditulis dalam C, mulai dari pembungkus driver perangkat hingga pustaka kompresi, sehingga kemampuan memanggil fungsi C dan menyediakan fungsi untuk C merupakan kebutuhan praktis pada pemrograman sistem. Topik ini membahas dua pragma kunci untuk keperluan itu, yaitu pragma Import dan pragma Export, bersama package Interfaces.C yang menyediakan tipe yang cocok dengan tipe C. Setelah topik ini diselesaikan, program gabungan Ada dan C dapat dikompilasi serta dijalankan dengan pemanggilan dari kedua arah.
Pragma Import untuk memanggil fungsi C#
Pragma Import menyatakan bahwa implementasi sebuah subprogram Ada ditulis dalam bahasa lain, sehingga badan subprogram tersebut tidak ditulis dalam Ada. Kompilator tidak menghasilkan kode untuk subprogram itu, dan simbolnya diselesaikan ketika program penghubung (linker) menyatukan berkas-berkas objek. Konvensi bahasa ditetapkan pada parameter pertama pragma, yaitu C untuk antarmuka yang kompatibel dengan C. Nama simbol yang benar-benar dicari linker dapat ditetapkan pada parameter ketiga, sedangkan tanpa parameter itu kompilator menggunakan nama subprogram Ada apa adanya.
Contoh berikut memanggil dua fungsi C dari prosedur utama Ada. Berkas pembantu.c berisi fungsi aritmetika sederhana, sedangkan panggil_c.adb mendeklarasikan kedua fungsi tersebut dengan tipe dari package Interfaces.C.
// tidak-dijalankan (dibangun bersama program Ada melalui blok bash)
#include <stddef.h>
#include <string.h>
int kuadrat (int n)
{
return n * n;
}
double rata_dua (double a, double b)
{
return (a + b) / 2.0;
}
size_t panjang (const char *s)
{
return strlen (s);
}with Ada.Text_IO;
with Ada.Strings.Fixed;
with Ada.Strings;
with Interfaces.C;
procedure Panggil_C is
function Kuadrat (N : Interfaces.C.int) return Interfaces.C.int;
pragma Import (C, Kuadrat, "kuadrat");
function Rata_Dua (A, B : Interfaces.C.double) return Interfaces.C.double;
pragma Import (C, Rata_Dua, "rata_dua");
function Gambar (Nilai : Interfaces.C.double) return String is
begin
return Ada.Strings.Fixed.Trim (Interfaces.C.double'Image (Nilai),
Ada.Strings.Both);
end Gambar;
begin
Ada.Text_IO.Put_Line ("kuadrat 12 = " & Interfaces.C.int'Image (Kuadrat (12)));
Ada.Text_IO.Put_Line ("rata dua 7.0 dan 12.0 = " & Gambar (Rata_Dua (7.0, 12.0)));
end Panggil_C;# kompilasi berkas C menjadi berkas objek
gcc -c pembantu.c
# kompilasi program Ada dan hubungkan dengan berkas objek C
gnatmake -q panggil_c.adb -largs pembantu.o
# jalankan program
./panggil_ckuadrat 12 = 144
rata dua 7.0 dan 12.0 = 9.50000000000000E+00Opsi -largs meneruskan argumen yang ditulis setelahnya kepada tahap penghubungan, sehingga berkas objek pembantu.o ikut disatukan. Alur kompilasinya sederhana, yaitu berkas C dikompilasi lebih dahulu dengan gcc, kemudian gnatmake mengompilasi berkas Ada dan menghubungkan keduanya. Nama fungsi pada pragma Import harus sama persis dengan nama fungsi di C, karena linker hanya mencocokkan teks nama tanpa mengetahui tipe parameternya.
Tipe data pada batas antarmuka#
Tipe pada deklarasi Ada harus cocok dengan tipe pada prototipe C agar data dibaca dengan benar. Package Interfaces.C menyediakan tipe yang ukuran dan maknanya sudah diselaraskan dengan C, sehingga pemilihan tipe pada batas antarmuka tidak perlu didasarkan pada tebakan. Tabel berikut merangkum pasangan yang paling sering digunakan.
| Tipe C | Tipe pada Interfaces.C |
|---|---|
int | Interfaces.C.int |
double | Interfaces.C.double |
size_t | Interfaces.C.size_t |
char* (string) | Interfaces.C.char_array |
String merupakan kasus khusus karena C memperlakukannya sebagai deretan karakter yang diakhiri karakter nol. Fungsi To_C mengubah tipe String Ada menjadi char_array yang siap dikirim, sedangkan fungsi To_Ada mengubah char_array tersebut kembali menjadi String. Contoh berikut menggunakan fungsi panjang pada berkas C yang sudah ada untuk menghitung panjang sebuah teks.
with Ada.Text_IO;
with Interfaces.C;
use Interfaces.C;
procedure Uji_String is
function Panjang (S : char_array) return size_t;
pragma Import (C, Panjang, "panjang");
Teks : constant char_array := To_C ("pemrograman sistem");
begin
Ada.Text_IO.Put_Line ("panjang teks = " & size_t'Image (Panjang (Teks)));
end Uji_String;# kompilasi ulang berkas C yang sudah tersedia
gcc -c pembantu.c
# kompilasi program dan hubungkan dengan berkas objek C
gnatmake -q uji_string.adb -largs pembantu.o
# jalankan program
./uji_stringpanjang teks = 18Nilai 18 sesuai dengan jumlah karakter pada teks pemrograman sistem, dan fungsi C menghitungnya dengan strlen dari pustaka standar tanpa mengetahui bahwa pemanggilnya ditulis dalam Ada. Pada batas antarmuka, kedua bahasa melihat memori yang sama, sehingga kesesuaian tipe menjadi syarat utama pertukaran data yang benar.
Pragma Export untuk dipanggil dari C#
Arah sebaliknya menggunakan pragma Export. Subprogram Ada yang diekspor diberi nama simbol C, sehingga fungsi C dapat memanggilnya seolah-olah fungsi tersebut ditulis dalam C. Kompilator tetap melakukan pemeriksaan tipe di sisi Ada, sedangkan sisi C hanya melihat prototipe biasa. Deklarasi yang diekspor diletakkan pada spesifikasi package agar sisi Ada dan sisi C merujuk satu deklarasi yang sama.
package Utilitas is
procedure Tambah (A : Integer; B : Integer; Hasil : out Integer);
pragma Export (C, Tambah, "tambah");
end Utilitas;package body Utilitas is
procedure Tambah (A : Integer; B : Integer; Hasil : out Integer) is
begin
Hasil := A + B;
end Tambah;
end Utilitas;// tidak-dijalankan (dibangun bersama unit Ada melalui blok bash)
#include <stdio.h>
extern void tambah (int a, int b, int *hasil);
int main (void)
{
int hasil;
tambah (12, 30, &hasil);
printf ("tambah (12, 30) = %d\n", hasil);
return 0;
}# kompilasi program utama C
gcc -c utama.c
# kompilasi package Ada
gnatmake -q utilitas.adb
# ikat seluruh unit Ada tanpa program utama Ada
gnatbind -n utilitas.ali
# hubungkan berkas objek C dengan hasil ikatan Ada
gnatlink utilitas.ali utama.o -o panggil_ada
# jalankan program hasil penghubungan
./panggil_adatambah (12, 30) = 42Ketika program utama ditulis dalam C, tahap ikatan (bind) dan penghubungan dilakukan secara terpisah karena gnatmake menganggap program utama selalu berbentuk prosedur Ada. Opsi -n pada gnatbind memerintahkan ikatan tanpa program utama Ada, kemudian gnatlink menyatukan berkas objek C dengan hasil ikatan tersebut. Parameter mode out pada prosedur Ada dipetakan menjadi penunjuk int* di sisi C, sesuai konvensi parameter keluaran.
Kesalahan umum#
Lima kekeliruan berikut paling sering muncul saat antarmuka C dipraktikkan.
- Lupa mengompilasi berkas C lebih dahulu. gnatmake tidak mengetahui berkas C mana yang dibutuhkan, sehingga tahap penghubungan berakhir dengan pesan
undefined reference. Kompilasi berkas C dengan gcc terlebih dahulu, lalu berikan berkas objeknya melalui-largs. - Nama simbol yang tidak cocok. Linker mencocokkan teks nama secara persis, sehingga
kuadratdanKuadratdianggap dua simbol yang berbeda. Samakan nama simbol pada pragma dengan deklarasi di sisi C. - Menggunakan tipe Ada biasa pada batas antarmuka. Tipe Integer pada host tidak dijamin sama dengan
intC pada target lain. Gunakan tipe dari Interfaces.C pada setiap parameter dan hasil antarmuka. - Mengirim tipe String langsung ke fungsi C. Tipe String Ada menyimpan informasi batasnya sendiri dan tidak diakhiri karakter nol. Ubah terlebih dahulu dengan
To_Cmenjadichar_array. - Menyandingkan subprogram Ada dengan fungsi C++ tanpa penyesuaian. Kompilator C++ mengubah nama fungsi menjadi simbol yang lebih panjang, sehingga nama pada pragma tidak pernah ditemukan linker. Tandai deklarasi C++ dengan
extern "C"atau batasi antarmuka pada berkas C murni.
Ringkasan#
- pragma Import menyatakan subprogram yang implementasinya ditulis dalam C, sedangkan pragma Export mengekspos subprogram Ada dengan nama simbol C.
- Package Interfaces.C menyediakan tipe yang cocok dengan C, bersama fungsi To_C dan To_Ada untuk string.
- Program gabungan dihubungkan dengan opsi -largs pada gnatmake, atau dengan gnatbind -n dan gnatlink apabila program utamanya ditulis dalam C.
- Kompilator tidak memeriksa kesesuaian dengan prototipe C, sehingga kesesuaian tipe menjadi tanggung jawab pemrogram.
Referensi#
- B.1 Interfacing Aspects — Ada Reference Manual 2022 — Mendefinisikan aspek Import dan Export beserta konvensi bahasa asing.
- B.3 Interfacing with C and C++ — Ada Reference Manual 2022 — Menetapkan package Interfaces.C beserta tipe dan konversinya.
- Interfacing With C — Introduction to Ada, AdaCore — Bab kursus resmi yang memperkenalkan pragma Import dan Export untuk C.
- GNAT User’s Guide — AdaCore — Mendokumentasikan opsi -largs pada gnatmake beserta tahap penghubungan.