Package generik (generic package) adalah package yang diparameterisasi atas tipe dan nilai, sehingga satu rancangan dapat diinstansiasi menjadi banyak package nyata. Mekanismenya sama dengan subprogram generik, tetapi skala abstrasinya lebih besar, yaitu seluruh deklarasi tipe dan operasi ikut diparameterisasi. Topik ini membahas bentuk spesifikasi dan badan package generik, cara menginstansiasinya untuk beberapa tipe berbeda, serta kesalahan yang muncul saat tipe aktual tidak memenuhi syaratnya. Contoh yang digunakan adalah tumpukan (stack) berkapasitas tetap untuk menyimpan nilai sensor suhu.
Spesifikasi package generik#
Spesifikasi package generik terdiri atas bagian generic yang memuat parameter formal dan spesifikasi package biasa setelahnya. Parameter formal boleh berupa tipe sebagaimana pada subprogram generik, dan boleh pula berupa objek bernilai tetap yang diisi saat instansiasi. Program tumpukan berikut menggunakan keduanya, yaitu Kapasitas bertipe Positive dan tipe formal T berkendala is private.
-- Spesifikasi package generik untuk tumpukan berkapasitas tetap.
generic
Kapasitas : Positive;
type T is private;
package Tumpukan_Generik is
type Tumpukan is private;
procedure Kosongkan (Item : out Tumpukan);
procedure Dorong (Item : in out Tumpukan; Nilai : T);
procedure Tarik (Item : in out Tumpukan; Nilai : out T);
function Tinggi (Item : Tumpukan) return Natural;
function Kosong (Item : Tumpukan) return Boolean;
function Penuh (Item : Tumpukan) return Boolean;
private
type Isi_Tumpukan is array (1 .. Kapasitas) of T;
type Tumpukan is record
Data : Isi_Tumpukan;
Banyak : Natural := 0;
end record;
end Tumpukan_Generik;Spesifikasi di atas mengekspor tipe Tumpukan beserta enam operasinya, sedangkan susunan datanya tersembunyi pada bagian privat, sebagaimana pola tipe privat. Perbedaannya dengan pola tipe privat biasa terletak pada bagian generic, karena ukuran array Isi_Tumpukan dan tipe elemennya baru ditentukan saat instansiasi. Karena kapasitas menjadi parameter, satu rancangan tumpukan melayani kebutuhan berbeda tanpa mengubah satu baris pun di dalam package.
Objek formal Kapasitas dideklarasikan tanpa mode, yang berarti mode in, dan nilainya menjadi tetap untuk setiap instansiasi. Array Isi_Tumpukan menggunakan rentang 1 .. Kapasitas, sehingga ukuran penyimpanannya mengikuti nilai yang diisi saat instansiasi. Objek formal semacam ini digunakan ketika rancangan membutuhkan konstanta yang boleh berbeda antarinstansiasi, sedangkan nilai yang sama untuk semua pengguna cukup dituliskan sebagai konstanta biasa di dalam package.
Badan dan dua instansiasi#
Badan package generik ditulis seperti badan package biasa dan berlaku untuk seluruh instansiasi yang berasal darinya. Program utama berikut menginstansiasi package yang sama untuk tipe Integer dengan kapasitas empat dan untuk tipe Float dengan kapasitas delapan. Setiap instansiasi menghasilkan package mandiri yang memiliki tipe Tumpukan sendiri, sehingga objek dari package yang berbeda tidak dapat dicampur.
Operasi pada badan di bawah ini menjaga dua batas tumpukan. Prosedur Dorong mengangkat Constraint_Error apabila tumpukan sudah penuh, sedangkan Tarik mengangkat exception yang sama apabila tumpukan kosong. Karena batas itu dijaga di satu tempat, program klien cukup memanggil fungsi Penuh dan Kosong apabila ingin menghindari exception, dan perlindungan yang sama berlaku untuk setiap instansiasi tanpa penyesuaian apa pun.
package body Tumpukan_Generik is
procedure Kosongkan (Item : out Tumpukan) is
begin
Item.Banyak := 0;
end Kosongkan;
procedure Dorong (Item : in out Tumpukan; Nilai : T) is
begin
if Item.Banyak = Kapasitas then
raise Constraint_Error;
end if;
Item.Banyak := Item.Banyak + 1;
Item.Data (Item.Banyak) := Nilai;
end Dorong;
procedure Tarik (Item : in out Tumpukan; Nilai : out T) is
begin
if Item.Banyak = 0 then
raise Constraint_Error;
end if;
Nilai := Item.Data (Item.Banyak);
Item.Banyak := Item.Banyak - 1;
end Tarik;
function Tinggi (Item : Tumpukan) return Natural is
begin
return Item.Banyak;
end Tinggi;
function Kosong (Item : Tumpukan) return Boolean is
begin
return Item.Banyak = 0;
end Kosong;
function Penuh (Item : Tumpukan) return Boolean is
begin
return Item.Banyak = Kapasitas;
end Penuh;
end Tumpukan_Generik;with Ada.Text_IO;
with Ada.Integer_Text_IO;
with Ada.Float_Text_IO;
with Tumpukan_Generik;
procedure Uji_Tumpukan is
-- Dua instansiasi dari satu package generik.
package Tumpukan_Integer is new Tumpukan_Generik
(Kapasitas => 4, T => Integer);
package Tumpukan_Float is new Tumpukan_Generik
(Kapasitas => 8, T => Float);
Tumpukan_A : Tumpukan_Integer.Tumpukan;
Tumpukan_B : Tumpukan_Float.Tumpukan;
Angka : Integer;
Suhu : Float;
begin
Tumpukan_Integer.Dorong (Tumpukan_A, 10);
Tumpukan_Integer.Dorong (Tumpukan_A, 24);
Tumpukan_Float.Dorong (Tumpukan_B, 22.5);
Tumpukan_Float.Dorong (Tumpukan_B, 21.5);
Ada.Text_IO.Put ("Tinggi tumpukan integer : ");
Ada.Integer_Text_IO.Put
(Tumpukan_Integer.Tinggi (Tumpukan_A), Width => 1);
Ada.Text_IO.New_Line;
Tumpukan_Integer.Tarik (Tumpukan_A, Angka);
Ada.Text_IO.Put ("Tarik tumpukan integer : ");
Ada.Integer_Text_IO.Put (Angka, Width => 1);
Ada.Text_IO.New_Line;
Tumpukan_Float.Tarik (Tumpukan_B, Suhu);
Ada.Text_IO.Put ("Tarik tumpukan float : ");
Ada.Float_Text_IO.Put (Suhu, Fore => 1, Aft => 2, Exp => 0);
Ada.Text_IO.New_Line;
Tumpukan_Float.Kosongkan (Tumpukan_B);
Ada.Text_IO.Put ("Tumpukan float kosong : ");
Ada.Text_IO.Put_Line (Boolean'Image (Tumpukan_Float.Kosong (Tumpukan_B)));
end Uji_Tumpukan;# kompilasi seluruh berkas program dari direktori bundle
gnatmake -q uji_tumpukan.adb
# jalankan program
./uji_tumpukanTinggi tumpukan integer : 2
Tarik tumpukan integer : 24
Tarik tumpukan float : 21.50
Tumpukan float kosong : TRUEKeluaran memperlihatkan kedua tumpukan bekerja secara terpisah, yaitu tumpukan Integer menampung dua nilai dan tumpukan Float mengeluarkan 21.50 dari puncaknya. Perintah gnatmake -q uji_tumpukan.adb menemukan spesifikasi dan badan package generik di direktori yang sama, sehingga tidak ada langkah kompilasi tambahan. Operasi pada setiap instansiasi dipanggil dengan nama package hasil instansiasi, misalnya Tumpukan_Integer.Dorong, agar package mana yang dimaksud selalu jelas.
Dua operasi terakhir memperlihatkan pengembalian keadaan awal, yaitu prosedur Kosongkan mengatur ulang jumlah data menjadi nol, dan fungsi Kosong menegaskan keadaan kosong tersebut dengan keluaran TRUE. Atribut Image pada tipe Boolean menghasilkan tulisan huruf kapital, sebagaimana Image pada tipe enumerasi lain. Fungsi Penuh mengikuti pola yang sama, yaitu membandingkan jumlah data dengan kapasitas yang menjadi parameter instansiasi.
Kesalahan umum#
Kesalahan yang sering muncul pada package generik adalah menginstansiasi dengan tipe aktual yang tidak memenuhi syarat penggunaannya. Kendala is private menuntut tipe aktual yang pasti (definite), yaitu tipe yang ukurannya diketahui saat instansiasi, karena tipe itu menjadi komponen array di dalam record.
Dua kelompok pesan error perlu dibedakan pada instansiasi, yaitu pesan yang menyebut lokasi di dalam spesifikasi generik dan pesan yang menyebut baris instansiasinya. Kelompok pertama muncul karena kendala formal dilanggar oleh tipe aktual, sedangkan kelompok kedua berkaitan dengan pengisian argumen saat instansiasi. Pesan pada contoh berikut memuat keduanya sekaligus.
with Tumpukan_Generik;
-- Berkas ini sengaja salah: menginstansiasi dengan tipe tak terkendala.
procedure Instansiasi_String_Error is
package Tumpukan_Teks is new Tumpukan_Generik
(Kapasitas => 4, T => String);
begin
null;
end Instansiasi_String_Error;# kompilasi berkas yang menginstansiasi dengan tipe String
gnatmake -q instansiasi_string_error.adbinstansiasi_string_error.adb:5:04: error: instantiation error at tumpukan_generik.ads:21
instansiasi_string_error.adb:5:04: error: unconstrained element type in array declaration
instansiasi_string_error.adb:6:28: error: actual for "T" must be a definite subtype
gnatmake: "instansiasi_string_error.adb" compilation errorPesan error menyatakan bahwa tipe aktual untuk T wajib berupa subtipe pasti, karena String adalah tipe tak terkendala (unconstrained) yang panjangnya belum ditentukan. Array Isi_Tumpukan memang tidak dapat menyimpan string yang ukurannya tidak tetap. Perbaikannya adalah menggunakan tipe string yang ukurannya sudah pasti, misalnya tipe turunan type Teks_Pendek is new String (1 .. 16);, atau tipe Bounded_String dari package Ada.Strings.Bounded.
Kesalahan kedua berkaitan dengan instansiasi ganda. Menuliskan is new dua kali dengan nama package yang sama menimbulkan error deklarasi ganda, sedangkan nama instansiasi yang berbeda menghasilkan dua package yang sah. Beri nama instansiasi yang menggambarkan tipe atau kegunaannya, misalnya Tumpukan_Integer dan Tumpukan_Float pada contoh di atas, supaya setiap pemanggilan operasinya mudah dibaca.
Ringkasan#
- Spesifikasi package generik memuat bagian
genericdengan parameter formal berupa tipe dan objek, sedangkan badannya berlaku untuk seluruh instansiasi. - Instansiasi dengan
is newmenghasilkan package mandiri yang tipenya berbeda dari instansiasi lain, sekalipun tipe aktualnya sama. - Tipe aktual untuk kendala
is privatewajib berupa tipe pasti, sehingga tipe tak terkendala sepertiStringditolak kompilator.
Referensi#
- Ada Reference Manual 2022, Bagian 12.1 Generic Declarations — Ada Resource Association — Menetapkan bentuk deklarasi generik, termasuk parameter formal tipe dan objek pada package generik.
- Generics — AdaCore — Menunjukkan package generik spesifikasi dan badan beserta instansiasinya dengan GNAT.