Package generik

Package generik sebagai abstraksi pakai ulang yang diinstansiasi per tipe.

Daftar isi

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.

ada tumpukan_generik.ads
--  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.

ada tumpukan_generik.adb
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;
ada uji_tumpukan.adb
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;
bash
# kompilasi seluruh berkas program dari direktori bundle
gnatmake -q uji_tumpukan.adb
# jalankan program
./uji_tumpukan
text Keluaran
Tinggi tumpukan integer : 2
Tarik tumpukan integer  : 24
Tarik tumpukan float    : 21.50
Tumpukan float kosong   : TRUE

Keluaran 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.

ada instansiasi_string_error.adb
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;
bash
# kompilasi berkas yang menginstansiasi dengan tipe String
gnatmake -q instansiasi_string_error.adb
text Keluaran
instansiasi_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 error

Pesan 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 generic dengan parameter formal berupa tipe dan objek, sedangkan badannya berlaku untuk seluruh instansiasi.
  • Instansiasi dengan is new menghasilkan package mandiri yang tipenya berbeda dari instansiasi lain, sekalipun tipe aktualnya sama.
  • Tipe aktual untuk kendala is private wajib berupa tipe pasti, sehingga tipe tak terkendala seperti String ditolak kompilator.

Referensi#