Pemrograman generik di Ada ── menulis kontrak dengan tipe, dan mewujudkan reuse tanpa biaya runtime
· Diperbarui pada: · Go Komura · Ada, Programming Language, Generics, Type System, Static Typing, Contract Model, Zero-Cost Abstraction, GNAT, Alire, Keandalan tinggi, Reuse kode
1. Pendahuluan ── bukan «menerima apa pun», melainkan «apa yang dijanjikan»
Ketika Anda mencoba menulis kode yang dapat di-reuse dalam bahasa bertipe statis, Anda segera berhadapan dengan dilema yang sama. Stack yang ditulis untuk integer ingin dipakai juga untuk string. Pemrosesan statistik yang sama ingin dipakai juga pada array floating-point. Logika pengurutan menaik ingin dipakai juga untuk pengurutan menurun. Namun jika kode yang sama disalin per tipe, perbaikan mudah terlewat. Sebaliknya, desain yang menerima apa pun lewat void* atau cast meruntuhkan type-safety.
Jawaban Ada adalah generic (generic units).
Generic Ada bukan sekadar substitusi teks. Ia menerima tipe, nilai, subprogram, bahkan paket itu sendiri sebagai parameter formal, dan tipenya diperiksa secara statis pada saat instansiasi. Dengan kata lain, alih-alih meneliti di runtime «apakah tipe ini benar-benar aman», ini adalah mekanisme yang pada waktu kompilasi menetapkan «apakah komponen ini memenuhi kontrak ini».
flowchart LR
accTitle: Tiga cara mereuse pemrosesan
accDescr: Diagram yang membandingkan salin-tempel, void*/Object/cast, dan generic Ada dari segi type-safety, pemeriksaan waktu kompilasi, dan biaya runtime
A[Pemrosesan yang ingin di-reuse] --> B{Bagaimana cara reuse-nya?}
B --> C[Salin dan tempel]
B --> D[void* / Object / cast]
B --> E[Generic Ada]
C --> C1[Mudah ada perbaikan yang terlewat]
D --> D1[Mudah terjadi error runtime atau rusaknya type-safety]
E --> E1[Type-safe]
E --> E2[Pemeriksaan waktu kompilasi]
E --> E3[Tanpa dispatch tambahan di runtime]
Artikel ini menata pemrograman generik di Ada menurut alur berikut.
- Subprogram generik
- Paket generik
- Parameter tipe, parameter nilai, dan parameter subprogram
- Kategori tipe seperti
private,range <>,digits <> - Contoh implementasi pengurutan, stack, pemrosesan statistik,
Count_If, dan penyimpanan kunci-nilai - Generic tingkat tinggi lewat parameter paket formal
- Contract model Ada dan gagasan desain praktis
1.1 Audiens dan apa yang dapat dibawa pulang dari artikel ini
Artikel ini ditujukan kepada pembaca seperti berikut.
- Anda yang punya pengalaman memakai template C++, generics C# atau Java, atau generics Rust
- Anda yang belum akrab dengan sintaksis Ada, tetapi tertarik pada gagasan desain «menulis kontrak dengan tipe»
- Anda yang sedang menimbang pedoman desain komponen reuse di bidang pemeliharaan jangka panjang, embedded, atau keandalan tinggi
Dari sisi sintaksis Ada, cukup mengenal paket (pemisahan spesifikasi .ads dan body .adb) serta mode parameter in / out / in out untuk mengikuti bacaan. Jika dua hal itu masih terasa goyah, membaca dulu «Daya tarik bahasa Ada» akan mempercepat pemahaman.
Yang dibawa pulang bukan sintaksis Ada itu sendiri, melainkan disiplin desain: menuliskan sebagai tipe, sebelum implementasi, apa yang dituntut sebuah komponen yang dapat di-reuse. Gagasan ini dapat langsung diterapkan juga saat memutuskan seberapa jauh menulis batasan interface di C# atau concepts di C++20.
1.2 Panduan membaca ── tidak perlu membaca semuanya
Artikel ini terdiri dari 20 bab. Tanpa harus dibaca berurutan, Anda dapat memetik bab sesuai tujuan sebagai berikut.
| Tujuan | Bab yang dibaca |
|---|---|
| Ingin menangkap gagasannya saja dalam waktu singkat | Bab 4 (model dasar) → bab 6 (subprogram generik minimum) → bab 13 (contract model) |
| Ingin menulis komponen sendiri | Bab 4 → bab 6 → bab 7 (paket generik) → bab 8 (injeksi perilaku) → bab 9 (kategori tipe) |
| Hanya butuh pedoman keputusan desain | Bab 13 → bab 14 (apa yang dijadikan generic) → bab 15 (titik tersandung) → bab 17 (daftar periksa) |
| Ingin langsung praktik | Siapkan lingkungan di bab 3, lalu mulai dari sampel jadi di bab 6 dan 7 |
Rute terpendek adalah tiga bab: 4, 6, dan 13. Tiga bab itu merangkum inti: parameter formal, instansiasi, dan contract model. Bab 5 dan 9 adalah daftar parameter formal, jadi cukup dibaca sebagai kamus ketika dibutuhkan.
Tema ini merupakan kelanjutan serial blog ini: «Daya tarik bahasa Ada», «Pengantar verifikasi formal dengan SPARK», «Konkurensi yang aman», dan «Sistem real-time». Kami mendalami gagasan Ada yang «berbicara desain lewat tipe» dari sudut generic.
Peta pengetahuan artikel ini
Generic Ada adalah mekanisme reuse yang menerima tipe, nilai, subprogram, dan paket sebagai parameter formal, lalu memeriksa tipenya secara statis pada saat instansiasi dengan new. Subprogram generik dan paket generik adalah dua pilar mekanisme ini: parameter tipe formal menyatakan kontrak operasi yang boleh dipakai body lewat kategori seperti private, range <>, dan digits <>; parameter subprogram formal menginjeksikan perilaku seperti perbandingan dan predikat; parameter paket formal menerima paket generik yang sudah diinstansiasi sebagai komponen. Berkat contract model yang memeriksa body secara mandiri hanya dalam lingkup parameter formal, masalah error yang baru diketahui saat instansiasi, yang secara historis dimiliki template C++, secara struktural tidak terjadi di Ada. Dengan GNAT dan Alire, mekanisme ini dapat dicoba di lingkungan tanpa biaya.
flowchart LR
accTitle: Peta pengetahuan pemrograman generik Ada
accDescr: Diagram yang menunjukkan subprogram generik dan paket generik yang dibangun di atas kontrak (contract model) parameter formal berupa tipe, nilai, subprogram, dan paket, pemeriksaan kontrak itu pada instansiasi dengan new, serta perbedaannya dengan masalah error saat instansiasi yang secara historis dimiliki template C++
ada_generics["generic Ada (unit generic)"]
ada_generic_contract_model["contract model generic Ada"]
ada["Ada (bahasa pemrograman)"]
ada_generic_subprogram["subprogram generic (Generic Subprogram)"]
ada_generic_package["paket generic (Generic Package)"]
ada_generic_formal_type["parameter tipe formal (Formal Type)"]
ada_generic_formal_subprogram["parameter subprogram formal (Formal Subprogram)"]
ada_generic_formal_package["parameter paket formal (Formal Package)"]
ada_generic_instantiation["instansiasi (instantiation)"]
ada_generic_formal_object["parameter objek formal (Formal Object)"]
late_instantiation_error["error template yang hanya diketahui saat instansiasi"]
cpp_templates["template C++"]
gnat["GNAT"]
alire["Alire"]
ada -->|"mengimplementasikan"| ada_generics
ada_generics -->|"menggunakan"| ada_generic_subprogram
ada_generics -->|"menggunakan"| ada_generic_package
ada_generic_subprogram -.->|"mensyaratkan"| ada_generic_formal_type
ada_generics -->|"menggunakan"| ada_generic_formal_subprogram
ada_generics -->|"menggunakan"| ada_generic_formal_package
ada_generics -->|"mensyaratkan"| ada_generic_instantiation
ada_generic_formal_type -->|"dikonfigurasi dengan"| ada_generic_instantiation
ada_generic_package -.->|"mensyaratkan"| ada_generic_formal_type
ada_generic_package -.->|"mensyaratkan"| ada_generic_formal_object
ada_generic_formal_subprogram -->|"mengimplementasikan"| ada_generic_contract_model
ada_generic_formal_type -->|"mengimplementasikan"| ada_generic_contract_model
ada_generic_contract_model -->|"mencegah"| late_instantiation_error
cpp_templates -.->|"dapat menyebabkan"| late_instantiation_error
ada_generic_formal_package -->|"mensyaratkan"| ada_generic_instantiation
gnat -->|"mengimplementasikan"| ada
gnat -.->|"dikonfigurasi dengan"| alire
ada_generic_subprogram -.->|"menggunakan"| ada_generic_formal_subprogram
ada_generic_package -.->|"menggunakan"| ada_generic_formal_subprogram
ada_generic_instantiation -.->|"mensyaratkan"| ada_generic_formal_object
ada_generic_formal_object -->|"mengimplementasikan"| ada_generic_contract_model
Pada diagram, garis utuh menunjukkan relasi yang selalu berlaku dan garis putus-putus menunjukkan relasi bersyarat (syaratnya ada pada penjelasan masing-masing relasi di halaman rincian). Daftar lengkap relasi (total 21, beserta bukti dan tingkat kepastian) serta definisi konsep utama dikumpulkan di halaman rincian peta pengetahuan (dalam bahasa Jepang). Data: JSON-LD / Turtle
2. Peta artikel ini
Pertama, tangkap gambaran keseluruhan lewat diagram. Memahami generic Ada hanya sebagai «fitur yang menerima tipe sebagai argumen» adalah pandangan yang terlalu sempit. Pada praktiknya, sesuai unit yang ingin di-reuse, Anda mengombinasikan subprogram, paket, parameter subprogram, parameter nilai, dan parameter paket formal.
mindmap
root((Ada Generics))
Generic Subprogram
Swap
Count_If
Sort
Generic Package
Stack
Statistics
KV Store
Formal Parameters
Tipe
private
limited private
range box
mod box
digits box
delta box
discrete box
Object
Max_Size
Threshold
Subprogram
fungsi Less
fungsi Equals
Predicate
Package
with package P is new Generic
Ide desain
Contract Model
Pemeriksaan statis
Zero-Cost Abstraction
Pemisahan spesifikasi dan body
box pada diagram merujuk pada <> Ada (box compound delimiter). Karena Mermaid tidak dapat menggambar <> apa adanya, pada diagram saja ia ditulis box.
Cara membaca artikel ini sederhana. Bab-bab awal mengikuti sintaksis; bab-bab kemudian membahas keputusan desain. Jika ini pertama kali Anda membaca Ada, jangan berusaha menghafal detail sintaksis sejak awal; perhatikan «apa yang dijadikan parameter formal» dan «operasi apa yang diizinkan pada parameter formal itu».
2.1 Kamus mini istilah
Istilah yang akan berulang muncul disusun dulu dalam padanan Indonesia–Inggris. Literatur Ada berbahasa Jepang sering memakai istilah yang setara «generic» (sōshō); artikel ini memakai «subprogram generik» dan «paket generik», tetapi maknanya sama dengan «generic».
| Notasi dalam artikel ini | Inggris | Arti |
|---|---|---|
| unit generik | generic unit | Deklarasi yang dimulai dengan generic. Sebutan gabungan untuk subprogram generik dan paket generik |
| parameter formal | generic formal parameter | Argumen di sisi penerima, ditulis di antara generic dan body deklarasi. Dapat berupa tipe, nilai, subprogram, atau paket |
| formal part | generic formal part | Bagian tempat parameter formal disusun. Boleh dibaca sebagai «tempat menulis kontrak» |
| parameter aktual | generic actual parameter | Tipe, nilai, subprogram, atau paket yang benar-benar diteruskan saat instansiasi |
| instansiasi | instantiation | Membuat subprogram atau paket biasa dari unit generik dengan new |
| box | <> |
Nama simbol <> dalam Ada. Seperti pada range <>, menyatakan «tipe konkret diputuskan saat instansiasi» |
| contract model | contract model | Cara Ada menulis body hanya dalam lingkup janji parameter formal, dan memeriksa tipe body secara mandiri (bab 13) |
Dalam teks kami menulis range <>, digits <> sesuai notasi Ada, tetapi pada diagram saja kami menulis range box, digits box. Alasannya: Mermaid tidak dapat menggambar label yang berisi <> apa adanya, dan maknanya sama dengan teks. Seperti pada tabel di atas, Ada sendiri menyebut <> sebagai box, jadi notasi diagram pun tidak menyimpang dari istilah Ada.
3. Lingkungan eksekusi dan cara kompilasi
Kode dalam artikel ini mengasumsikan GNAT 15.x atau lebih baru. GNAT adalah compiler Ada yang representatif, dan dapat dipasang dari Alire. Alire adalah package manager Ada / SPARK, dan juga dipakai untuk mengelola toolchain serta build.
gnat --version
# GNAT 15.2.1
Pasang GNAT dari Alire (package manager Ada) dengan alr install gnat_native gprbuild, lalu masukkan ke PATH.
Sampel yang dibahas artikel ini diasumsikan diletakkan di dalam repositori sebagai berikut.
flowchart TB
accTitle: Tata letak sampel di repositori
accDescr: Diagram yang menunjukkan bahwa sampel artikel diletakkan sebagai berkas .ada terpisah di bawah ada-generic-programming/src/snippets/
R[ada-generic-programming/] --> S[src/]
S --> N[snippets/]
N --> A[01_swap.ada]
N --> B[02_stack.ada]
N --> C[03_sort.ada]
N --> D[04_statistics.ada]
N --> E[05_filter.ada]
N --> F[06_kv_store.ada]
R --> README[README.md]
Sampel yang merangkum beberapa unit kompilasi dalam satu berkas dipisah dulu dengan gnatchop, lalu dikompilasi dengan gnatmake.
mkdir work
cd work
gnatchop ../src/snippets/01_swap.ada
gnatmake -gnata swap_demo
./swap_demo
-gnata adalah opsi yang mengaktifkan assertion. Opsi ini tidak wajib hanya untuk memakai generic, tetapi pada sampel pembelajaran memudahkan pemeriksaan kontrak dan kondisi batas.
sequenceDiagram
accTitle: Alur dari satu berkas .ada sampai dijalankan
accDescr: Urutan pengembang menyerahkan satu berkas .ada ke gnatchop, mem-build dengan gnatmake, lalu menjalankan berkas eksekusi
participant Dev as Pengembang
participant Chop as gnatchop
participant Build as gnatmake
participant Exe as Berkas eksekusi
Dev->>Chop: Menyerahkan satu berkas .ada
Chop-->>Dev: Dipisah menjadi .ads / .adb / main
Dev->>Build: gnatmake -gnata main
Build-->>Dev: Bind dan link sampai selesai
Dev->>Exe: ./main
Exe-->>Dev: Hasil eksekusi
4. Model dasar generic Ada
Generic Ada paling mudah dipahami sebagai tiga langkah berikut.
- Menulis unit generik
- Menulis parameter formal pada bagian
generic - Menginstansiasi dengan
newdi sisi pemakai
flowchart LR
accTitle: Tiga langkah model generic Ada
accDescr: Alur dari deklarasi generic, parameter formal, dan body generik sampai instansiasi dengan new, lalu pemakaian sebagai subprogram atau paket biasa
G[deklarasi generic] --> F[parameter formal]
F --> B[body generik]
B --> I[instansiasi dengan new]
I --> U[dipakai sebagai subprogram atau paket biasa]
F --> F1[tipe]
F --> F2[nilai]
F --> F3[subprogram]
F --> F4[paket]
Misalnya, jika pemrosesan yang menukar dua nilai dijadikan generic, hanya tipe yang perlu dijadikan parameter formal.
generic
type Element is private;
procedure Generic_Swap (A, B : in out Element);
Pada titik ini, Generic_Swap belum dapat dipanggil. Ini adalah «template pemrosesan tukar yang dapat dipakai untuk sembarang tipe Element». Baru setelah diberi tipe konkret ia menjadi prosedur biasa.
procedure Swap_Integer is new Generic_Swap (Integer);
Dalam bentuk diagram, hubungannya sebagai berikut.
flowchart TB
accTitle: Instansiasi Generic_Swap per tipe
accDescr: Diagram yang menunjukkan Generic_Swap diinstansiasi menjadi prosedur biasa untuk Integer, Character, dan My_Record
Template[Generic_Swap<br/>type Element is private] -->|meneruskan Integer| SwapInt[Swap_Integer]
Template -->|meneruskan Character| SwapChar[Swap_Character]
Template -->|meneruskan My_Record| SwapRecord[Swap_My_Record]
SwapInt --> ICall[Menukar variabel Integer]
SwapChar --> CCall[Menukar variabel Character]
SwapRecord --> RCall[Menukar variabel My_Record]
Yang penting adalah body template hanya ditulis dengan operasi yang boleh dipakai pada Element. Jika dideklarasikan type Element is private;, operasi dasar seperti assignment dan perbandingan kesetaraan dapat dipakai, tetapi perbandingan urutan dan aritmetika tidak. Dengan kata lain, deklarasi generic itu sendiri menyatakan «apa yang boleh diasumsikan komponen ini».
5. Jenis parameter formal ── kosakata generic Ada
Yang dapat diterima generic Ada bukan hanya tipe. Inilah titik yang sangat berbeda dibanding generics biasa di C# atau Java.
flowchart TB
accTitle: Empat jenis parameter formal generic Ada
accDescr: Diagram yang menguraikan parameter formal generic menjadi parameter tipe, objek/nilai, subprogram, dan paket beserta contoh penulisannya
P[generic formal parameters] --> T[parameter tipe]
P --> O[parameter objek / nilai]
P --> S[parameter subprogram]
P --> PKG[parameter paket]
T --> T1[type Element is private]
T --> T2[type Index is box]
T --> T3[type Real is digits box]
O --> O1[Max_Size : Positive]
O --> O2[Default_Value : Element]
S --> S1[with function Less...]
S --> S2[with procedure Put ...]
PKG --> P1[with package P is new ...]
box pada diagram merujuk pada <> Ada (box compound delimiter). Karena Mermaid tidak dapat menggambar <> apa adanya, pada diagram saja ia ditulis box.
Parameter formal yang representatif, dalam bentuk tabel, adalah sebagai berikut.
| Jenis | Contoh | Arti |
|---|---|---|
| Parameter tipe | type Element is private; |
Bentuk dasar yang menerima sembarang tipe definite non-limited |
| Parameter tipe limited | type Element is limited private; |
Juga menerima tipe yang tidak dapat disalin |
| Tipe diskret | type Index is (<>); |
Tipe yang dapat dipakai sebagai indeks array, seperti integer atau enumerasi |
| Integer bertanda | type Count is range <>; |
Boleh mengasumsikan aritmetika integer seperti +, -, dan perbandingan urutan |
| Integer modular | type Word is mod <>; |
Menangani operasi bit atau integer bersifat modulus |
| Tipe floating-point | type Real is digits <>; |
Float, Long_Float, tipe floating-point buatan pengguna, dan sebagainya |
| Tipe fixed-point | type Money is delta <>; |
Menangani aritmetika fixed-point |
| Parameter nilai | Max_Size : Positive; |
Menetapkan ukuran, ambang, dan semacamnya per instance |
| Subprogram | with function Predicate (...) return Boolean; |
Menginjeksikan perilaku seperti fungsi perbandingan atau predikat |
| Paket | with package P is new Some_Generic (<>); |
Menerima paket generik yang sudah diinstansiasi sebagai komponen |
Karena kosakata ini ada, di Ada penulisan yang natural bukan «menerima apa pun lalu melakukan hal berbahaya di dalamnya», melainkan «hanya menerima tipe yang dapat melakukan operasi ini».
6. Subprogram generik ── memahami struktur minimum dengan Generic_Swap
Sebagai contoh pertama, lihat Generic_Swap yang menukar dua variabel sembarang tipe.
6.1 Spesifikasi
generic
type Element is private;
procedure Generic_Swap (A, B : in out Element);
Bagian yang mengikuti generic adalah parameter formal. Di sini diterima sebuah tipe bernama Element. is private berarti, dari sudut pandang body generik, representasi internal tipe itu tidak diketahui.
Dari deklarasi ini dapat dibaca dua hal berikut.
Generic_Swapdapat dipakai untuk sembarang tipeElement- Body tidak bergantung pada struktur internal
Elementmaupun perbandingan urutan
6.2 Body
procedure Generic_Swap (A, B : in out Element) is
Temp : constant Element := A;
begin
A := B;
B := Temp;
end Generic_Swap;
Body ini hanya memakai assignment pada Element. Tidak memakai A < B maupun A + B. Karena itu, ia dapat dipakai secara natural pada tipe yang mendukung assignment: Integer, Character, tipe record, tipe enumerasi, dan sebagainya.
flowchart LR
accTitle: Pertukaran dua nilai lewat Generic_Swap
accDescr: Alur Temporary menyalin A, A mengambil B, lalu B mengambil Temporary, sehingga 10 dan 20 tertukar
subgraph Before[Sebelum pemanggilan]
A1[A = 10]
B1[B = 20]
end
A1 --> T[Temp = A]
B1 --> A2[A = B]
T --> B2[B = Temp]
subgraph After[Setelah pemanggilan]
A2[A = 20]
B2[B = 10]
end
6.3 Instansiasi
Di sisi pemakai, gunakan new.
procedure Swap_Int is new Generic_Swap (Integer);
procedure Swap_Char is new Generic_Swap (Character);
Dengan ini, Swap_Int dan Swap_Char dapat dipanggil sebagai prosedur biasa.
with Ada.Text_IO; use Ada.Text_IO;
procedure Swap_Demo is
generic
type Element is private;
procedure Generic_Swap (A, B : in out Element);
procedure Generic_Swap (A, B : in out Element) is
Temp : constant Element := A;
begin
A := B;
B := Temp;
end Generic_Swap;
procedure Swap_Int is new Generic_Swap (Integer);
X : Integer := 10;
Y : Integer := 20;
begin
Put_Line ("Before: X=" & Integer'Image (X) & ", Y=" & Integer'Image (Y));
Swap_Int (X, Y);
Put_Line ("After : X=" & Integer'Image (X) & ", Y=" & Integer'Image (Y));
end Swap_Demo;
Gambaran eksekusinya sebagai berikut.
Before: X= 10, Y= 20
After : X= 20, Y= 10
Di sini, variabel Float tidak dapat diteruskan ke Swap_Int (X, Y);. Swap_Int adalah prosedur biasa yang sudah diinstansiasi untuk Integer. Generic lebih mudah dipahami bukan sebagai «lubang yang menampung apa pun», melainkan sebagai «mekanisme yang membuat benda konkret yang aman per tipe».
7. Paket generik ── menjadikan tipe dan nilai sebagai parameter
Jika yang ingin di-reuse bukan satu subprogram, melainkan kumpulan beberapa operasi plus status internal, gunakan paket generik. Contoh representatifnya adalah stack.
Pada stack, logika dasarnya sama; yang berubah hanya tipe elemen dan ukuran maksimum.
flowchart TB
accTitle: Generic_Stack menghasilkan instance per tipe dan ukuran
accDescr: Diagram yang menunjukkan Generic_Stack menerima Element_Type dan Max_Size, menyediakan Push/Pop dan operasi lain, lalu menghasilkan instance seperti Int_Stack dan Float_Stack
G[Generic_Stack] --> P1[Element_Type]
G --> P2[Max_Size]
G --> Ops[Push / Pop / Size / Is_Empty / Is_Full]
G --> I1[Int_Stack<br/>Element=Integer<br/>Max_Size=5]
G --> I2[Float_Stack<br/>Element=Float<br/>Max_Size=3]
G --> I3[String_Stack<br/>Element=Unbounded_String<br/>Max_Size=20]
7.1 Spesifikasi
generic
type Element_Type is private;
Max_Size : Positive;
package Generic_Stack is
procedure Push (Item : Element_Type);
function Pop return Element_Type;
function Is_Empty return Boolean;
function Is_Full return Boolean;
function Size return Natural;
Stack_Overflow : exception;
Stack_Underflow : exception;
end Generic_Stack;
Di sini dipakai dua jenis parameter formal.
Element_Typeadalah parameter tipeMax_Sizeadalah parameter nilai
Karena Max_Size bertipe Positive, instansiasi dengan ukuran 0 atau kurang tidak dapat dilakukan. Seperti ini, parameter nilai pun dapat diberi batasan lewat tipe.
7.2 Body
package body Generic_Stack is
subtype Index_Type is Positive range 1 .. Max_Size;
type Storage_Type is array (Index_Type) of Element_Type;
Data : Storage_Type;
Top : Natural := 0;
procedure Push (Item : Element_Type) is
begin
if Top = Max_Size then
raise Stack_Overflow;
end if;
Top := Top + 1;
Data (Top) := Item;
end Push;
function Pop return Element_Type is
Result : Element_Type;
begin
if Top = 0 then
raise Stack_Underflow;
end if;
Result := Data (Top);
Top := Top - 1;
return Result;
end Pop;
function Is_Empty return Boolean is
begin
return Top = 0;
end Is_Empty;
function Is_Full return Boolean is
begin
return Top = Max_Size;
end Is_Full;
function Size return Natural is
begin
return Top;
end Size;
end Generic_Stack;
Yang penting pada body paket ini: Data dan Top dibuat terpisah per instance.
package Int_Stack is new Generic_Stack (Integer, 5);
package Float_Stack is new Generic_Stack (Float, 3);
Keduanya dibuat dari template yang sama, tetapi status internal tidak dibagi.
flowchart LR
accTitle: Int_Stack dan Float_Stack tidak berbagi status
accDescr: Diagram yang menunjukkan dua instance dari Generic_Stack masing-masing memiliki Top dan Data sendiri
Template[Generic_Stack] --> IntStack[Int_Stack]
Template --> FloatStack[Float_Stack]
subgraph I[Status Int_Stack]
ITop[Top]
IData[Data : array Integer]
end
subgraph F[Status Float_Stack]
FTop[Top]
FData[Data : array Float]
end
IntStack --> I
FloatStack --> F
7.3 Transisi status stack
Stack lebih mudah dipahami jika dilihat sebagai mesin status.
stateDiagram-v2
accTitle: Transisi status stack
accDescr: Diagram status yang menunjukkan transisi Empty, NonEmpty, Full, Overflow, dan Underflow lewat Push dan Pop
[*] --> Empty
Empty --> NonEmpty: Push
NonEmpty --> NonEmpty: Push / Pop
NonEmpty --> Empty: Pop mengambil elemen terakhir
NonEmpty --> Full: Push mencapai Max_Size
Full --> NonEmpty: Pop
Full --> Overflow: Push
Empty --> Underflow: Pop
7.4 Contoh pemakaian
with Ada.Text_IO; use Ada.Text_IO;
procedure Stack_Demo is
generic
type Element_Type is private;
Max_Size : Positive;
package Generic_Stack is
procedure Push (Item : Element_Type);
function Pop return Element_Type;
function Is_Empty return Boolean;
function Is_Full return Boolean;
function Size return Natural;
Stack_Overflow : exception;
Stack_Underflow : exception;
end Generic_Stack;
package body Generic_Stack is
subtype Index_Type is Positive range 1 .. Max_Size;
type Storage_Type is array (Index_Type) of Element_Type;
Data : Storage_Type;
Top : Natural := 0;
procedure Push (Item : Element_Type) is
begin
if Top = Max_Size then
raise Stack_Overflow;
end if;
Top := Top + 1;
Data (Top) := Item;
end Push;
function Pop return Element_Type is
Result : Element_Type;
begin
if Top = 0 then
raise Stack_Underflow;
end if;
Result := Data (Top);
Top := Top - 1;
return Result;
end Pop;
function Is_Empty return Boolean is (Top = 0);
function Is_Full return Boolean is (Top = Max_Size);
function Size return Natural is (Top);
end Generic_Stack;
package Int_Stack is new Generic_Stack (Integer, 5);
begin
Int_Stack.Push (10);
Int_Stack.Push (20);
Int_Stack.Push (30);
Put_Line ("Size=" & Natural'Image (Int_Stack.Size));
Put_Line ("Pop =" & Integer'Image (Int_Stack.Pop));
Put_Line ("Pop =" & Integer'Image (Int_Stack.Pop));
Put_Line ("Size=" & Natural'Image (Int_Stack.Size));
end Stack_Demo;
Build dan jalankan dengan langkah yang sama seperti bab 3.
gnatchop ../src/snippets/02_stack.ada
gnatmake -gnata stack_demo
./stack_demo
Size= 3
Pop = 30
Pop = 20
Size= 1
Spasi satu karakter di kanan = berasal dari spesifikasi 'Image pada tipe integer, yang menaruh satu spasi di depan nilai non-negatif. Karena Push dipanggil 3 kali dan Pop 2 kali, Size terakhir menjadi 1. Selain itu, jika Push dipanggil lagi ketika stack penuh (dalam contoh ini 5 elemen sesuai Max_Size), Int_Stack.Stack_Overflow dilempar; jika Pop dipanggil ketika kosong, Int_Stack.Stack_Underflow dilempar.
Paket generik dalam praktik sering efektif sebagai «kontainer kecil», «buffer panjang tetap», «ring buffer», «antrean log», atau «lapisan abstraksi perangkat keras». Khususnya di Ada, desain yang menetapkan ukuran secara statis sebagai tipe atau parameter nilai, alih-alih menggerakkannya di runtime, selaras dengan sistem berkeandalan tinggi.
8. Parameter subprogram formal ── menginjeksikan perilaku
Meski hanya menerima tipe, masih ada hal yang belum dapat diekspresikan. Misalnya pada pengurutan, selain tipe elemen, diperlukan juga logika perbandingan «mana yang diletakkan lebih dulu».
Di Ada, fungsi perbandingan itu dapat dijadikan parameter formal generic.
flowchart LR
accTitle: Generic_Insertion_Sort menerima fungsi perbandingan
accDescr: Diagram yang menunjukkan Generic_Insertion_Sort menerima Item_Type, Index, Item_Array, dan fungsi perbandingan, lalu menghasilkan urutan menaik, menurun, atau kustom
A[Generic_Insertion_Sort] --> T[Item_Type]
A --> I[Index]
A --> ARR[Item_Array]
A --> CMP[fungsi perbandingan]
CMP --> ASC[memakai perbandingan standar]
CMP --> DESC[meneruskan Greater untuk urutan menurun]
CMP --> CUSTOM[meneruskan urutan kustom]
8.1 Spesifikasi
generic
type Item_Type is private;
type Index is (<>);
type Item_Array is array (Index range <>) of Item_Type;
with function "<" (Left, Right : Item_Type) return Boolean is <>;
procedure Generic_Insertion_Sort (Items : in out Item_Array);
Di sini ada empat parameter formal.
Item_Type: tipe elemen arrayIndex: tipe indeks arrayItem_Array: tipe array yang sebenarnya"<": fungsi perbandingan
type Index is (<>); menerima tipe diskret. Bukan hanya tipe integer, enumerasi pun dapat diterima. Yang khas Ada: indeks array tidak harus Positive; enumerasi seperti Day juga dapat dipakai.
is <> pada with function "<" ... is <>; berarti, jika parameter aktual dihilangkan, operator standar yang terlihat atau fungsi yang cocok akan dipakai. Jadi, untuk tipe yang sudah punya < seperti Integer, fungsi perbandingan tidak perlu dituliskan secara eksplisit.
8.2 Body
procedure Generic_Insertion_Sort (Items : in out Item_Array) is
J : Index;
Key : Item_Type;
begin
if Items'Length <= 1 then
return;
end if;
for I in Index'Succ (Items'First) .. Items'Last loop
Key := Items (I);
J := I;
while J > Items'First and then Key < Items (Index'Pred (J)) loop
Items (J) := Items (Index'Pred (J));
J := Index'Pred (J);
end loop;
Items (J) := Key;
end loop;
end Generic_Insertion_Sort;
Insertion sort tidak cocok untuk array besar, tetapi cocok untuk menjelaskan generic. Hanya dengan mengganti fungsi perbandingan, struktur loop yang sama dapat dipakai untuk urutan menaik maupun menurun.
flowchart TB
accTitle: Alur insertion sort
accDescr: Diagram yang menunjukkan Key diambil dari kiri, elemen sebelumnya digeser ke kanan selama Key berada di depannya, lalu Key disisipkan sampai array terurut
Start[Array belum terurut] --> Pick[Ambil Key dari kiri ke kanan]
Pick --> Compare{Key berada sebelum elemen sebelumnya? }
Compare -->|Yes| Shift[Geser elemen sebelumnya ke kanan]
Shift --> Compare
Compare -->|No| Insert[Sisipkan Key]
Insert --> Done{Sudah sampai akhir?}
Done -->|No| Pick
Done -->|Yes| End[Array terurut]
8.3 Membuat urutan menaik dan menurun dari body yang sama
type Int_Array is array (Positive range <>) of Integer;
procedure Sort_Asc is new Generic_Insertion_Sort
(Item_Type => Integer,
Index => Positive,
Item_Array => Int_Array);
function Greater (Left, Right : Integer) return Boolean is
(Left > Right);
procedure Sort_Desc is new Generic_Insertion_Sort
(Item_Type => Integer,
Index => Positive,
Item_Array => Int_Array,
"<" => Greater);
Sort_Asc memakai < standar. Sementara Sort_Desc mengganti fungsi perbandingan dengan "<" => Greater.
flowchart LR
accTitle: Urutan menaik dan menurun dari body yang sama
accDescr: Diagram yang menunjukkan array yang sama diurutkan menaik dengan perbandingan standar dan menurun dengan Greater
Data[99, 3, 47, 12] --> A[Sort_Asc<br/>perbandingan standar]
Data --> D[Sort_Desc<br/>Greater sebagai fungsi perbandingan]
A --> AO[3, 12, 47, 99]
D --> DO[99, 47, 12, 3]
Mekanisme ini dekat dengan desain C++ yang meneruskan comparison function object sebagai argumen template, atau desain Rust yang menuntut urutan lewat trait bound. Namun di Ada, hal itu dieksplisitkan sebagai parameter subprogram formal: «teruskan fungsi berbentuk ini».
8.4 Contoh eksekusi
Mengikuti struktur bab 18, sisi pemanggil ketika Generic_Insertion_Sort diletakkan di berkas terpisah (generic_insertion_sort.ads / .adb) adalah sebagai berikut.
with Ada.Text_IO; use Ada.Text_IO;
with Generic_Insertion_Sort;
procedure Sort_Demo is
type Int_Array is array (Positive range <>) of Integer;
function Greater (Left, Right : Integer) return Boolean is
(Left > Right);
procedure Sort_Asc is new Generic_Insertion_Sort
(Item_Type => Integer,
Index => Positive,
Item_Array => Int_Array);
procedure Sort_Desc is new Generic_Insertion_Sort
(Item_Type => Integer,
Index => Positive,
Item_Array => Int_Array,
"<" => Greater);
procedure Show (Label : String; Items : Int_Array) is
begin
Put (Label);
for V of Items loop
Put (Integer'Image (V));
end loop;
New_Line;
end Show;
Asc : Int_Array := (99, 3, 47, 12);
Desc : Int_Array := (99, 3, 47, 12);
begin
Sort_Asc (Asc);
Sort_Desc (Desc);
Show ("Asc :", Asc);
Show ("Desc:", Desc);
end Sort_Demo;
gnatchop ../src/snippets/03_sort.ada
gnatmake -gnata sort_demo
./sort_demo
Asc : 3 12 47 99
Desc: 99 47 12 3
Dapat dikonfirmasi bahwa dua prosedur yang dibuat dari body Generic_Insertion_Sort yang sama menjadi urutan terbalik hanya dengan mengganti fungsi perbandingan. Spasi di depan setiap elemen berasal dari 'Image tipe integer yang menaruh satu spasi di depan nilai non-negatif.
9. Kategori tipe ── menulis kontrak yang lebih konkret daripada private
type T is private; nyaman, tetapi tidak berarti semuanya bisa. Pada tipe private, aritmetika empat operasi atau perbandingan urutan tidak dapat dipakai seolah-olah sudah ada. Karena itu, di Ada kategori dapat ditentukan pada parameter tipe formal.
flowchart TB
accTitle: Kategori yang dapat ditentukan pada parameter tipe formal
accDescr: Diagram yang menguraikan formal type menjadi private, limited private, discrete, range, mod, digits, delta, dan access, plus contoh turunannya
FormalType[Formal Type] --> Private[private]
FormalType --> Limited[limited private]
FormalType --> Discrete[discrete box: tipe diskret]
FormalType --> Signed[range box: integer bertanda]
FormalType --> Modular[mod box: integer modular]
FormalType --> Float[digits box: floating-point]
FormalType --> Fixed[delta box: fixed-point]
FormalType --> Access[tipe access]
Discrete --> Enum[tipe enumerasi]
Discrete --> Int[tipe integer]
Float --> F1[Float]
Float --> F2[Long_Float]
Float --> F3[tipe floating-point buatan pengguna]
box pada diagram merujuk pada <> Ada (box compound delimiter). Karena Mermaid tidak dapat menggambar <> apa adanya, pada diagram saja ia ditulis box.
9.1 Apa manfaat menentukan kategori
Misalnya untuk menghitung rata-rata atau varians, diperlukan penjumlahan, pengurangan, perkalian, dan pembagian. Pada tipe private, operasi-operasi itu tidak dapat diasumsikan. Karena itu, kita membatasi pada tipe floating-point.
generic
type Real is digits <>;
type Real_Array is array (Positive range <>) of Real;
package Generic_Statistics is
function Mean (Values : Real_Array) return Real;
function Variance (Values : Real_Array) return Real;
end Generic_Statistics;
Dengan type Real is digits <>;, diketahui bahwa Real adalah tipe floating-point. Karena itu, di body generik dapat dipakai +, -, *, /, dan sebagainya.
9.2 Body
package body Generic_Statistics is
function Mean (Values : Real_Array) return Real is
Sum : Real := 0.0;
begin
if Values'Length = 0 then
return 0.0;
end if;
for V of Values loop
Sum := Sum + V;
end loop;
return Sum / Real (Values'Length);
end Mean;
function Variance (Values : Real_Array) return Real is
M : constant Real := Mean (Values);
Sum : Real := 0.0;
begin
if Values'Length = 0 then
return 0.0;
end if;
for V of Values loop
declare
D : constant Real := V - M;
begin
Sum := Sum + D * D;
end;
end loop;
return Sum / Real (Values'Length);
end Variance;
end Generic_Statistics;
9.3 Dipakai dengan Float dan Long_Float
type Float_Array is array (Positive range <>) of Float;
type Long_Array is array (Positive range <>) of Long_Float;
package Float_Stats is new Generic_Statistics (Float, Float_Array);
package Long_Stats is new Generic_Statistics (Long_Float, Long_Array);
Pemrosesan statistik yang sama dapat di-reuse pada tipe floating-point dengan presisi berbeda.
flowchart LR
accTitle: Generic_Statistics diinstansiasi untuk beberapa presisi
accDescr: Diagram yang menunjukkan Generic_Statistics menghasilkan Float_Stats, Long_Stats, dan My_Real_Stats, masing-masing menghitung Mean dan Variance
Stats[Generic_Statistics<br/>Real is digits box] --> FS[Float_Stats]
Stats --> LS[Long_Stats]
Stats --> MS[My_Real_Stats]
FS --> FCalc[Mean / Variance dengan Float]
LS --> LCalc[Mean / Variance dengan Long_Float]
MS --> MCalc[Mean / Variance dengan Real buatan pengguna]
9.4 Contoh eksekusi
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Generic_Statistics;
procedure Statistics_Demo is
type Float_Array is array (Positive range <>) of Float;
package Float_Stats is new Generic_Statistics (Float, Float_Array);
Samples : constant Float_Array := (1.0, 2.0, 3.0, 4.0);
begin
Put ("Mean = ");
Put (Float_Stats.Mean (Samples), Fore => 1, Aft => 3, Exp => 0);
New_Line;
Put ("Variance = ");
Put (Float_Stats.Variance (Samples), Fore => 1, Aft => 3, Exp => 0);
New_Line;
end Statistics_Demo;
gnatchop ../src/snippets/04_statistics.ada
gnatmake -gnata statistics_demo
./statistics_demo
Mean = 2.500
Variance = 1.250
Ada.Float_Text_IO adalah pustaka standar yang menginstansiasi Ada.Text_IO.Float_IO untuk Float; dengan Exp => 0, notasi menjadi desimal biasa, bukan bentuk eksponen. Aft => 3 adalah jumlah digit di belakang koma. Perhatikan bahwa Variance ini adalah varians populasi (dibagi Values'Length), bukan varians sampel.
9.5 Penentuan kategori sebagai «spesifikasi tingkat tipe»
Penentuan kategori bukan sekadar sintaksis untuk menenangkan compiler. Bagi pembaca, ia juga menjadi spesifikasi yang menyampaikan «apa yang dituntut komponen ini».
| Pemrosesan yang ingin ditulis | Tipe formal yang cocok | Alasan |
|---|---|---|
| Tukar, simpan, ambil | private |
Cukup dapat di-assign |
| Pengelolaan resource yang tidak dapat disalin | limited private |
Tidak mengasumsikan assignment |
| Indeks array, penelusuran status enumerasi | (<>) |
Dapat memakai First, Last, Succ, Pred |
| Jumlah integer, counter | range <> |
Boleh mengasumsikan aritmetika integer |
| Bit mask, counter berputar | mod <> |
Boleh mengasumsikan aritmetika modular |
| Rata-rata, varians, perhitungan numerik | digits <> |
Boleh mengasumsikan aritmetika floating-point |
| Presisi tetap seperti uang atau besaran kendali | delta <> |
Boleh mengasumsikan aritmetika fixed-point |
10. Injeksi predikat ── menulis Count_If dengan gaya Ada
Parameter subprogram formal tidak hanya untuk fungsi perbandingan, tetapi juga untuk predikat. Predikat adalah fungsi yang menerima nilai dan mengembalikan Boolean.
Peran yang dekat dengan Func<T, bool> di C#, Predicate<T> di Java, atau lambda dan function object di C++, di Ada dapat diekspresikan sebagai subprogram formal generic.
10.1 Spesifikasi
generic
type Element is private;
type Index is (<>);
type Array_Type is array (Index range <>) of Element;
with function Predicate (Item : Element) return Boolean;
function Generic_Count_If (Arr : Array_Type) return Natural;
Di sini Predicate tidak diberi is <>. Karena tidak ada fungsi predikat standar yang secara otomatis terlihat, desainnya mewajibkan sisi pemakai untuk selalu meneruskannya.
10.2 Body
function Generic_Count_If (Arr : Array_Type) return Natural is
Count : Natural := 0;
begin
for Item of Arr loop
if Predicate (Item) then
Count := Count + 1;
end if;
end loop;
return Count;
end Generic_Count_If;
Alur pemrosesannya sederhana.
flowchart LR
accTitle: Alur Generic_Count_If
accDescr: Diagram yang menunjukkan setiap elemen array diuji Predicate, Count bertambah jika True, lalu Count dikembalikan
Arr[array] --> Loop[telusuri setiap elemen]
Loop --> P{"Predicate(Item)?"}
P -->|True| Inc[tambah Count]
P -->|False| Skip[tidak melakukan apa-apa]
Inc --> Next[ke elemen berikutnya]
Skip --> Next
Next --> Result[kembalikan Count]
10.3 Hitung genap dan hitung menurut ambang
type Int_Array is array (Positive range <>) of Integer;
function Is_Even (N : Integer) return Boolean is
(N mod 2 = 0);
function Is_Large (N : Integer) return Boolean is
(N > 50);
function Count_Even is new Generic_Count_If
(Element => Integer,
Index => Positive,
Array_Type => Int_Array,
Predicate => Is_Even);
function Count_Large is new Generic_Count_If
(Element => Integer,
Index => Positive,
Array_Type => Int_Array,
Predicate => Is_Large);
Dari logika penelusuran yang sama dapat dibuat dua fungsi yang hanya berbeda kondisinya.
flowchart TB
accTitle: Dua fungsi dari Generic_Count_If
accDescr: Diagram yang menunjukkan Count_Even dan Count_Large dibuat dari Generic_Count_If dengan predikat berbeda, lalu menghitung genap dan nilai lebih besar dari 50
G[Generic_Count_If] --> E[Count_Even<br/>Predicate = Is_Even]
G --> L[Count_Large<br/>Predicate = Is_Large]
Data[12, 7, 88, 3, 56, 91, 44, 19, 62] --> E
Data --> L
E --> ER[jumlah bilangan genap]
L --> LR[jumlah yang lebih besar dari 50]
Dalam contoh ini, penelusuran array, pengelolaan counter, dan pengembalian hasil semuanya sama. Yang diinjeksikan sebagai fungsi hanyalah «apa yang dihitung». Inilah bentuk dasar desain higher-order di Ada.
10.4 Contoh eksekusi
with Ada.Text_IO; use Ada.Text_IO;
with Generic_Count_If;
procedure Count_If_Demo is
type Int_Array is array (Positive range <>) of Integer;
function Is_Even (N : Integer) return Boolean is
(N mod 2 = 0);
function Is_Large (N : Integer) return Boolean is
(N > 50);
function Count_Even is new Generic_Count_If
(Element => Integer,
Index => Positive,
Array_Type => Int_Array,
Predicate => Is_Even);
function Count_Large is new Generic_Count_If
(Element => Integer,
Index => Positive,
Array_Type => Int_Array,
Predicate => Is_Large);
Data : constant Int_Array := (12, 7, 88, 3, 56, 91, 44, 19, 62);
begin
Put_Line ("Even =" & Natural'Image (Count_Even (Data)));
Put_Line ("Large =" & Natural'Image (Count_Large (Data)));
end Count_If_Demo;
gnatchop ../src/snippets/05_filter.ada
gnatmake -gnata count_if_demo
./count_if_demo
Even = 5
Large = 4
Dari 9 elemen Data, yang genap ada 5 buah: 12, 88, 56, 44, 62; yang lebih besar dari 50 ada 4 buah: 88, 56, 91, 62.
11. Menyintesis beberapa parameter ── penyimpanan kunci-nilai generik
Pada komponen nyata, jarang sekali cukup dengan satu parameter tipe. Tipe kunci dan nilai, cara membandingkan kunci, jumlah entri maksimum, dan kondisi lain perlu dikombinasikan.
Di sini kita memakai contoh penyimpanan kunci-nilai sederhana berukuran tetap.
flowchart TB
accTitle: Parameter dan pemakaian Generic_KV_Store
accDescr: Diagram yang menunjukkan Generic_KV_Store menerima tipe kunci/nilai, fungsi kesetaraan, dan Max_Entries, lalu dipakai sebagai store konfigurasi, cache kecil, atau kamus panjang tetap
KV[Generic_KV_Store] --> K[Key_Type]
KV --> V[Value_Type]
KV --> EQ[fungsi kesetaraan kunci]
KV --> M[Max_Entries]
KV --> Ops[Put / Get / Contains]
Ops --> Use1[penyimpanan nilai konfigurasi]
Ops --> Use2[cache skala kecil]
Ops --> Use3[kamus panjang tetap untuk embedded]
11.1 Spesifikasi
generic
type Key_Type is private;
type Value_Type is private;
with function "=" (Left, Right : Key_Type) return Boolean is <>;
Max_Entries : Positive := 50;
package Generic_KV_Store is
procedure Put (Key : Key_Type; Val : Value_Type);
function Get (Key : Key_Type) return Value_Type;
function Contains (Key : Key_Type) return Boolean;
Key_Not_Found : exception;
Store_Full : exception;
end Generic_KV_Store;
Paket ini punya empat parameter formal.
| Parameter | Jenis | Peran |
|---|---|---|
Key_Type |
tipe | Tipe kunci |
Value_Type |
tipe | Tipe nilai |
"=" |
subprogram | Penentuan kesetaraan kunci |
Max_Entries |
nilai | Jumlah entri maksimum |
Max_Entries diberi nilai default := 50. Jadi, jika tidak ditentukan, store-nya berkapasitas 50 entri.
11.2 Body
package body Generic_KV_Store is
subtype Index_Type is Positive range 1 .. Max_Entries;
type Key_Array is array (Index_Type) of Key_Type;
type Value_Array is array (Index_Type) of Value_Type;
type Used_Array is array (Index_Type) of Boolean;
Keys : Key_Array;
Values : Value_Array;
Used : Used_Array := (others => False);
function Find_Index (Key : Key_Type) return Natural is
begin
for I in Index_Type loop
if Used (I) and then Keys (I) = Key then
return I;
end if;
end loop;
return 0;
end Find_Index;
function Find_Free return Natural is
begin
for I in Index_Type loop
if not Used (I) then
return I;
end if;
end loop;
return 0;
end Find_Free;
procedure Put (Key : Key_Type; Val : Value_Type) is
Pos : Natural := Find_Index (Key);
begin
if Pos = 0 then
Pos := Find_Free;
if Pos = 0 then
raise Store_Full;
end if;
Used (Pos) := True;
Keys (Pos) := Key;
end if;
Values (Pos) := Val;
end Put;
function Get (Key : Key_Type) return Value_Type is
Pos : constant Natural := Find_Index (Key);
begin
if Pos = 0 then
raise Key_Not_Found;
end if;
return Values (Pos);
end Get;
function Contains (Key : Key_Type) return Boolean is
begin
return Find_Index (Key) /= 0;
end Contains;
end Generic_KV_Store;
Implementasi ini memakai pencarian linear, jadi tidak ditujukan untuk data berjumlah besar. Namun bentuknya mudah dipakai di situasi yang mengutamakan panjang tetap, skala kecil, dan tanpa alokasi memori dinamis.
sequenceDiagram
accTitle: Alur Put dan Get pada Generic_KV_Store
accDescr: Urutan pemanggil memanggil Put, instance mencari kunci yang ada atau slot kosong, lalu Get mengembalikan nilai
participant App as pemanggil
participant Store as instance Generic_KV_Store
participant Data as Keys/Values/Used
App->>Store: Put(Key, Value)
Store->>Data: Find_Index(Key)
alt kunci sudah ada
Store->>Data: Values(Pos) := Value
else kunci baru
Store->>Data: Find_Free
Store->>Data: Keys(Pos) := Key
Store->>Data: Values(Pos) := Value
Store->>Data: Used(Pos) := True
end
App->>Store: Get(Key)
Store->>Data: Find_Index(Key)
Data-->>Store: Pos
Store-->>App: Values(Pos)
11.3 Contoh instansiasi
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Generic_KV_Store;
procedure KV_Demo is
package Int_String_Store is new Generic_KV_Store
(Key_Type => Integer,
Value_Type => Unbounded_String,
Max_Entries => 10);
begin
Int_String_Store.Put (1, To_Unbounded_String ("Ada"));
Int_String_Store.Put (2, To_Unbounded_String ("SPARK"));
if Int_String_Store.Contains (1) then
Put_Line ("1 => " & To_String (Int_String_Store.Get (1)));
else
Put_Line ("1 => (not found)");
end if;
-- Put ke kunci yang sudah ada akan menimpa
Int_String_Store.Put (1, To_Unbounded_String ("Ada 2022"));
Put_Line ("1 => " & To_String (Int_String_Store.Get (1)));
if Int_String_Store.Contains (9) then
Put_Line ("9 => " & To_String (Int_String_Store.Get (9)));
else
Put_Line ("9 => (not found)");
end if;
end KV_Demo;
gnatchop ../src/snippets/06_kv_store.ada
gnatmake -gnata kv_demo
./kv_demo
1 => Ada
1 => Ada 2022
9 => (not found)
Jika Get dipanggil tanpa memastikan keberadaan lewat Contains, Key_Not_Found dilempar ketika kunci tidak ada. Pilih salah satu: cabangkan dengan Contains seperti contoh di atas, atau tulis exception handler.
"=" dihilangkan. Integer punya operator kesetaraan standar, dan is <> membuat operator itu dipakai.
Jika kuncinya misalnya string yang mengabaikan huruf besar/kecil, Anda dapat meneruskan fungsi kesetaraan sendiri.
function Same_Key (Left, Right : Unbounded_String) return Boolean is
(To_Lower (To_String (Left)) = To_Lower (To_String (Right)));
package String_Key_Store is new Generic_KV_Store
(Key_Type => Unbounded_String,
Value_Type => Integer,
"=" => Same_Key,
Max_Entries => 100);
12. Parameter paket formal ── memodulkan generic lebih jauh
Pada generic Ada, paket itu sendiri dapat dijadikan parameter formal. Dengan ini, «instance yang dibuat dari suatu paket generik» dapat diperlakukan sebagai masukan generic lain.
flowchart LR
accTitle: Instance stack menjadi masukan logger
accDescr: Diagram yang menunjukkan Int_Stack dari Generic_Stack diteruskan ke Generic_Stack_Logger lalu menjadi instance logger
GS[Generic_Stack] --> IS[Int_Stack]
IS --> Logger[Generic_Stack_Logger]
Logger --> Logged[Int_Stack_Logger_Instance]
12.1 Logger yang menerima stack
Misalnya, kita membuat logger yang menerima instance Generic_Stack sebelumnya dan menampilkan ukurannya.
generic
with package Stack is new Generic_Stack (<>);
package Generic_Stack_Logger is
procedure Print_Size;
end Generic_Stack_Logger;
Bodynya sebagai berikut.
with Ada.Text_IO; use Ada.Text_IO;
package body Generic_Stack_Logger is
procedure Print_Size is
begin
Put_Line ("Stack size =" & Natural'Image (Stack.Size));
end Print_Size;
end Generic_Stack_Logger;
Di sisi pemakai, buat stack dulu, lalu teruskan stack itu ke logger.
package Int_Stack is new Generic_Stack
(Element_Type => Integer,
Max_Size => 10);
package Int_Stack_Logger is new Generic_Stack_Logger
(Stack => Int_Stack);
Dengan desain seperti ini, komponen generik dapat dikombinasikan satu sama lain.
flowchart TB
accTitle: Dua tahap kombinasi generic
accDescr: Diagram yang menunjukkan tahap 1 membuat Int_Stack dari Generic_Stack, lalu tahap 2 membuat Int_Stack_Logger yang menyediakan Print_Size
subgraph Layer1[Tahap 1]
T1[Generic_Stack] --> I1[Int_Stack]
end
subgraph Layer2[Tahap 2]
T2[Generic_Stack_Logger] --> I2[Int_Stack_Logger]
end
I1 --> T2
I2 --> API[Print_Size]
Kegunaannya dekat dengan template template parameter di C++, tetapi di Ada dapat dieksplisitkan bahwa «instance paket generik ini yang diterima». Pada kode Ada berskala besar, ini berguna ketika memisahkan lalu mengombinasikan kontainer, algoritme, log, pemeriksaan, dan bantuan tes.
13. Contract Model ── gagasan terpenting generic Ada
Yang penting untuk memahami generic Ada adalah contract model.
Body generik harus ditulis hanya dengan operasi yang dijanjikan parameter formal. Misalnya, meski hanya dideklarasikan type Element is private;, < tidak dapat dipakai pada Element. Jika ingin memakai <, itu harus dieksplisitkan sebagai subprogram formal, atau kategori tipe dibuat lebih konkret.
flowchart TB
accTitle: Contract model generic Ada
accDescr: Diagram yang menunjukkan body diperiksa hanya dalam lingkup kontrak formal part, lalu instansiasi memeriksa apakah actual memenuhi kontrak
Spec[generic formal part<br/>kontrak] --> Body[generic body<br/>implementasi dalam lingkup kontrak]
Body --> Check1[pemeriksaan tipe pada body saja]
Spec --> Inst[instansiasi]
Actual[actual parameters<br/>tipe, fungsi, dan nilai aktual] --> Inst
Inst --> Check2[periksa apakah actual memenuhi kontrak]
Check2 --> Instance[paket/subprogram biasa]
Desain ini melindungi bukan hanya pemakai generic, tetapi juga penulis generic.
13.1 Perbedaan tampilan dengan template C++
Template C++ kuat, tetapi secara historis punya sifat «error baru muncul setelah body template diinstansiasi». Hal itu diperbaiki oleh concepts di C++20, namun generic Ada sejak awal adalah model yang menuliskan kontrak secara eksplisit.
flowchart LR
accTitle: Perbedaan pemeriksaan kontrak Ada dan C++
accDescr: Diagram yang membandingkan alur Ada yang menulis kontrak di formal part lalu memeriksa body dan actual, dengan template C++ yang secara historis mewujudkan requirement saat instansiasi
subgraph Ada[Ada]
A1[tulis kontrak di formal part] --> A2[body diperiksa dalam kontrak]
A2 --> A3[instantiation memeriksa actual]
end
subgraph CPP[C++ templates]
C1[tulis template body] --> C2[requirement terwujud saat instantiation]
C2 --> C3[constraints dapat dieksplisitkan dengan concepts]
end
Generics Java dan C# berpusat pada tipe referensi, constraint, type erasure, dan hubungannya dengan representasi runtime. Generic Ada, sebaliknya, condong pada gagasan membuat instance konkret pada waktu kompilasi.
| Sudut pandang | Ada | C++ | Java | Rust |
|---|---|---|---|---|
| Cara menulis kontrak | Tipe, nilai, fungsi, dan paket di formal part | templates / concepts | Parameter tipe dan bounds | trait bounds |
| Pemeriksaan body | Diperiksa dalam kontrak parameter formal | Berpusat pada perwujudan saat instansiasi | Diperiksa dalam bounds | Diperiksa dalam trait bounds |
| Biaya runtime | Pada dasarnya resolusi statis | Pada dasarnya generasi statis | Terpengaruh type erasure | Pada dasarnya monomorphization |
| Parameter nilai | Ya | Ya | Terbatas | const generics |
| Subprogram sebagai parameter formal | Ya | Dinyatakan dengan function object, dll. | Lambda / functional interface | Closure / fungsi / trait |
| Paket sebagai parameter formal | Ya | Template template, dll. | Tidak | Terpisah dari struktur modul |
Fitur rinci tiap bahasa berbeda, tetapi ciri Ada adalah «menulis kontrak sebagai sintaksis terlebih dulu».
14. Keputusan desain di praktik ── apa yang sebaiknya dijadikan generic
Generic nyaman, tetapi bukan berarti segala sesuatu harus dijadikan generic. Dalam praktik, menilai seperti berikut mengurangi risiko gagal.
flowchart TB
accTitle: Cabang keputusan apa yang dijadikan generic
accDescr: Diagram yang menuntun dari perbedaan tipe, ukuran/ambang, perilaku perbandingan, dan kebutuhan merangkum status internal ke parameter tipe, parameter nilai, subprogram formal, atau paket generik
Start[Terdapat pemrosesan yang ingin di-reuse] --> Q1{Hanya tipenya yang berbeda?}
Q1 -->|Yes| GType[pertimbangkan parameter tipe]
Q1 -->|No| Q2{Ukuran atau ambang juga berbeda?}
Q2 -->|Yes| GObject[tambah parameter nilai]
Q2 -->|No| Q3{Perilaku perbandingan atau predikat berbeda?}
Q3 -->|Yes| GSubp[tambah subprogram formal]
Q3 -->|No| Q4{Ingin merangkum status internal dan API?}
Q4 -->|Yes| GPackage[paket generik]
Q4 -->|No| Normal[subprogram biasa sudah cukup]
14.1 Situasi yang cocok untuk subprogram generik
Subprogram generik cocok untuk algoritme tanpa status.
SwapSortCount_IfFind- Transformasi bergaya
Map Min/Max
Jika body algoritme pendek serta masukan dan keluaran jelas, subprogram lebih mudah dibaca daripada paket.
14.2 Situasi yang cocok untuk paket generik
Paket generik cocok ketika Anda ingin membawa beberapa operasi dan status internal bersama tipe.
- Stack panjang tetap
- Ring buffer
- Kamus skala kecil
- Set pemrosesan statistik
- Abstraksi I/O per perangkat
- Set operasi tipe numerik ber satuan
Khususnya di Ada, spesifikasi paket adalah API publik dan body paket adalah implementasi, sehingga paket generik dapat dipakai sebagai «template modul yang type-safe».
flowchart LR
accTitle: Spesifikasi, body, dan formal part paket generik
accDescr: Diagram yang menunjukkan formal part menjadi kontrak bagi spec dan body, spec dipakai pengguna, dan body disembunyikan
Spec[package spec<br/>API publik] --> User[sisi pemakai]
Body[package body<br/>implementasi internal] -.disembunyikan.-> User
Formal[generic formal part<br/>kontrak tipe, nilai, fungsi] --> Spec
Formal --> Body
14.3 Mulai dengan sedikit parameter formal
Jika parameter formal terlalu banyak, instansiasi menjadi sulit dibaca. Lebih aman memulai dari yang minimal, lalu menambah ketika ada alasan untuk mengganti.
-- contoh yang mudah menjadi sulit dibaca
package X is new Generic_Foo
(A, B, C, D, E, F, G);
-- sisakan niat dengan named association
package X is new Generic_Foo
(Element_Type => Integer,
Index_Type => Positive,
Buffer_Size => 128,
"<" => Less);
Di Ada, named association dapat dipakai saat instansiasi. Karena titik penting desain generic muncul pada instansiasi, dalam kode praktik menuliskannya secara named sering lebih mudah dipelihara.
15. Titik tersandung yang sering muncul
Generic Ada kuat, tetapi ada beberapa titik yang mudah tersandung di awal.
15.1 Tipe private tidak dapat dibandingkan urutannya
Body seperti berikut tidak dapat ditulis.
generic
type Element is private;
function Bad_Min (A, B : Element) return Element;
function Bad_Min (A, B : Element) return Element is
begin
if A < B then -- error di sini
return A;
else
return B;
end if;
end Bad_Min;
Karena Element hanya dideklarasikan sebagai private, tidak ada jaminan < dapat dipakai. Jika ingin membandingkan, tambahkan ke kontrak sebagai berikut.
generic
type Element is private;
with function "<" (Left, Right : Element) return Boolean is <>;
function Generic_Min (A, B : Element) return Element;
flowchart LR
accTitle: Perbandingan harus ditulis di formal part
accDescr: Diagram yang menunjukkan bahwa memakai perbandingan di body tanpa kontrak menghasilkan error kompilasi, sementara menuliskan fungsi perbandingan di formal part membuat komparabilitas diperiksa saat instansiasi
Need[ingin memakai perbandingan di body] --> Contract[tulis fungsi perbandingan di formal part]
Contract --> OK[konfirmasi komparabilitas saat instansiasi]
Need --> NoContract[hanya private]
NoContract --> Error[error kompilasi di body generik]
15.2 is <> bukan «inferensi otomatis untuk apa pun»
is <> nyaman, tetapi bukan sihir. Pada titik instansiasi, operator atau subprogram yang cocok harus terlihat. Jika fungsi perbandingan sendiri diletakkan di paket lain, yang aman adalah melakukan with atau use dengan benar, atau meneruskannya secara eksplisit dengan named association.
procedure Sort_By_Age is new Generic_Insertion_Sort
(Item_Type => Person,
Index => Positive,
Item_Array => Person_Array,
"<" => Younger_Than);
15.3 Exception juga menjadi benda berbeda per instance
Jika exception dideklarasikan di spesifikasi paket generik, exception itu menjadi berbeda per instance.
package Int_Stack is new Generic_Stack (Integer, 5);
package Float_Stack is new Generic_Stack (Float, 3);
Dalam kasus ini, Int_Stack.Stack_Overflow dan Float_Stack.Stack_Overflow diperlakukan sebagai exception yang berbeda. Jika ingin menanganinya sebagai exception bersama, pertimbangkan juga desain yang mendefinisikan exception di luar generic.
flowchart TB
accTitle: Exception per instance adalah benda berbeda
accDescr: Diagram yang menunjukkan Stack_Overflow Generic_Stack menjadi exception berbeda pada Int_Stack dan Float_Stack
Generic[Generic_Stack<br/>deklarasi Stack_Overflow] --> I[Int_Stack.Stack_Overflow]
Generic --> F[Float_Stack.Stack_Overflow]
I -.exception berbeda.-> F
15.4 Ukuran kode dapat bertambah
Generic mudah menghindari referensi tak langsung yang berlebih di runtime, tetapi karena instance dibuat per tipe, ukuran kode dapat bertambah jika jumlah instance banyak.
Ini trade-off yang juga terlihat pada template C++ dan monomorphization di Rust. Pada pengembangan yang condong ke keandalan tinggi, embedded, atau real-time, gagasannya adalah mengelola ukuran artefak build sebagai ganti mengurangi ketidakpastian runtime.
flowchart LR
accTitle: Satu body generic menghasilkan kode per instance
accDescr: Diagram yang menunjukkan satu body generic menghasilkan versi Integer, Float, Long_Float, dan My_Type, dengan manfaat menghindari type test runtime dan boxing, serta risiko ukuran kode bertambah
Generic[satu body generic] --> I1[versi Integer]
Generic --> I2[versi Float]
Generic --> I3[versi Long_Float]
Generic --> I4[versi My_Type]
I1 --> Code[kode yang dihasilkan]
I2 --> Code
I3 --> Code
I4 --> Code
Code --> Pros[mudah menghindari type test runtime dan boxing]
Code --> Cons[jika instance banyak, perhatikan peningkatan ukuran]
15.5 Situasi yang sebaiknya memakai limited private
type Element is private; mengasumsikan assignment. Jika menangani hal yang tidak ingin disalin — file handle, lock, device handle, dan semacamnya — pertimbangkan limited private.
generic
type Resource is limited private;
with procedure Close (R : in out Resource);
procedure Generic_Use_And_Close (R : in out Resource);
Pada desain yang menangani tipe yang tidak dapat disalin, lebih aman memakai algoritme yang menerapkan prosedur, atau desain yang mengekspresikan referensi secara eksplisit, daripada kontainer yang menyimpan nilai.
16. Kumpulan pola desain kecil
Dari sini, bentuk yang sering dipakai dalam praktik disusun secara singkat.
16.1 Menyediakan Min hanya untuk nilai yang dapat dibandingkan
generic
type Element is private;
with function "<" (Left, Right : Element) return Boolean is <>;
function Generic_Min (A, B : Element) return Element;
function Generic_Min (A, B : Element) return Element is
begin
if A < B then
return A;
else
return B;
end if;
end Generic_Min;
flowchart LR
accTitle: Generic_Min menuntut fungsi perbandingan
accDescr: Diagram yang menunjukkan Element membutuhkan fungsi perbandingan agar Generic_Min dapat mengembalikan yang lebih kecil
T[Element] --> C[perlu fungsi perbandingan]
C --> M[Generic_Min]
M --> R[kembalikan yang lebih kecil]
16.2 Menjadikan ambang sebagai parameter nilai
generic
type Count_Type is range <>;
Threshold : Count_Type;
function Generic_Is_Over (Value : Count_Type) return Boolean;
function Generic_Is_Over (Value : Count_Type) return Boolean is
begin
return Value > Threshold;
end Generic_Is_Over;
Parameter nilai cocok untuk nilai yang ingin ditetapkan sebagai sifat instance, bukan sebagai pengaturan runtime.
16.3 Menginjeksikan sarana keluaran
generic
type Element is private;
with procedure Put (Item : Element);
procedure Generic_Print_Twice (Item : Element);
procedure Generic_Print_Twice (Item : Element) is
begin
Put (Item);
Put (Item);
end Generic_Print_Twice;
Dengan bentuk ini, tujuan keluaran dapat diganti: keluaran standar, log, buffer untuk tes, dan sebagainya.
flowchart TB
accTitle: Generic_Print_Twice menerima Put
accDescr: Diagram yang menunjukkan Generic_Print_Twice menerima Put sebagai subprogram formal, lalu dapat diarahkan ke konsol, log, atau buffer tes
Print[Generic_Print_Twice] --> Put[menerima Put sebagai subprogram formal]
Put --> Console[keluaran konsol]
Put --> Log[keluaran log]
Put --> Test[buffer untuk tes]
16.4 Tidak mematok tipe indeks array
Di Ada, tipe indeks array juga merupakan informasi tipe yang penting. Alih-alih mematok Positive, menjadikan tipe indeks sebagai parameter formal bila perlu akan menaikkan kemampuan reuse.
generic
type Element is private;
type Index is (<>);
type Array_Type is array (Index range <>) of Element;
procedure Generic_Clear (Arr : in out Array_Type; Value : Element);
Dengan desain ini, bukan hanya array berindeks Positive, array berindeks enumerasi pun dapat ditangani.
flowchart LR
accTitle: Index diskret mencakup beberapa jenis
accDescr: Diagram yang menunjukkan Index diskret dapat berupa Positive range, enumerasi Day, enumerasi State, atau tipe integer sendiri
Index[Index is discrete] --> Positive[Positive range]
Index --> Day[enumerasi Day]
Index --> State[enumerasi State]
Index --> Slot[tipe integer sendiri]
17. Daftar periksa untuk API yang khas Ada
Ketika menulis generic, tinjau ulang dari sudut berikut agar lebih mudah dibaca.
flowchart TB
accTitle: Daftar periksa desain generic
accDescr: Diagram yang merangkum tujuh butir pemeriksaan: jumlah parameter formal, operasi di formal part, kategori tipe, named association, status dan exception per instance, ukuran kode, serta instance untuk tes
C[cek desain generic] --> C1[apakah parameter formal minimal?]
C --> C2[apakah operasi yang diperlukan dieksplisitkan di formal part?]
C --> C3[apakah kategori seperti private / range / digits sudah tepat?]
C --> C4[dapat diinstansiasi dengan named association yang mudah dibaca?]
C --> C5[sadar akan status dan exception per instance?]
C --> C6[peningkatan ukuran kode dapat diterima?]
C --> C7[sudah menyiapkan instance untuk tes?]
Dirangkum dalam kalimat, sebagai berikut.
- Operasi yang dipakai di body harus terlihat sebagai kontrak parameter formal.
- Jika
privatecukup, pakaiprivate. Jika perlu aritmetika, pakairange <>ataudigits <>. - Perilaku yang berubah per tipe — perbandingan, hash, keluaran, konversi — jadikan subprogram formal.
- Jika ukuran atau ambang adalah sifat instance, jadikan parameter nilai.
- Jika punya status, pikirkan dulu paket generik; jika tidak punya status, pikirkan dulu subprogram generik.
- Semakin banyak argumen instansiasi, semakin perlu named association.
- Rancang dengan asumsi bahwa exception dan status internal independen per instance.
18. Contoh struktur keseluruhan sampel
Jika sampel dalam artikel dipisah ke berkas, struktur berikut membuatnya lebih mudah dibaca.
flowchart TB
accTitle: Contoh pemisahan berkas spesifikasi, body, dan demo
accDescr: Diagram yang menunjukkan generic_swap dan lainnya di bawah src/generics, serta swap_demo dan demo lain di bawah src/demos
Root[ada-generic-programming] --> Src[src]
Src --> G[generics]
Src --> D[demos]
G --> SwapSpec[generic_swap.ads]
G --> SwapBody[generic_swap.adb]
G --> StackSpec[generic_stack.ads]
G --> StackBody[generic_stack.adb]
G --> SortSpec[generic_insertion_sort.ads]
G --> SortBody[generic_insertion_sort.adb]
G --> StatsSpec[generic_statistics.ads]
G --> StatsBody[generic_statistics.adb]
G --> CountSpec[generic_count_if.ads]
G --> CountBody[generic_count_if.adb]
G --> KVSpec[generic_kv_store.ads]
G --> KVBody[generic_kv_store.adb]
D --> SwapDemo[swap_demo.adb]
D --> StackDemo[stack_demo.adb]
D --> SortDemo[sort_demo.adb]
D --> StatsDemo[statistics_demo.adb]
D --> CountDemo[count_if_demo.adb]
D --> KVDemo[kv_demo.adb]
Untuk sampel artikel kecil, merangkum ke satu berkas lalu gnatchop juga nyaman, tetapi jika memikirkan praktik dan pemeliharaan jangka panjang, memisahkan spesifikasi .ads dan body .adb adalah struktur yang lebih khas Ada.
19. Ringkasan ── memutuskan batas reuse dengan tipe
Pemrograman generik di Ada bukan sekadar «fitur menulis kode yang tidak bergantung pada tipe». Esensinya justru menuliskan secara eksplisit, sebagai kontrak tipe, subprogram, dan nilai, apa yang dituntut komponen yang dapat di-reuse.
flowchart LR
accTitle: Dari kontrak sampai reuse yang type-safe
accDescr: Alur menulis kontrak, menulis body generik, menginstansiasi dengan tipe/nilai/fungsi, memakai secara type-safe, lalu reuse tanpa menyalin
Contract[tulis kontrak] --> Generic[tulis body generik]
Generic --> Instance[instansiasi dengan meneruskan tipe, nilai, fungsi]
Instance --> Safe[pakai secara type-safe]
Safe --> Reuse[reuse tanpa menyalin]
Seperti yang dilihat artikel ini, generic Ada dapat menerima hal berikut sebagai parameter formal.
- Tipe
- Nilai
- Subprogram
- Paket
Lebih jauh, untuk tipe dapat ditentukan kategori yang cukup rinci: private, limited private, range <>, mod <>, digits <>, delta <>, (<>), dan sebagainya. Dengan itu, body generik tidak bergantung pada «operasi yang belum jelas tersedia atau tidak», melainkan dapat diimplementasikan dengan aman hanya dengan operasi yang tertulis di kontrak.
Pada aset C atau C++ lama, reuse kadang memakai makro, void*, function pointer, atau percabangan tipe yang ditulis tangan. Generic Ada dapat mengganti banyak dari keperluan itu dengan bentuk yang type-safe dan mudah dibaca. Khususnya pada perangkat lunak pemeliharaan jangka panjang, embedded, real-time, dan keandalan tinggi, «desain yang memutuskan batas pada waktu kompilasi» ini punya nilai besar.
20. Area konsultasi terkait
KomuraSoft LLC menangani pengembangan aplikasi Windows, investigasi dan perbaikan aset yang ada, penataan batas COM / ActiveX / 32-bit / 64-bit, serta konsultasi teknis dan tinjauan desain. Selain desain yang condong ke pengetikan statis dan keandalan tinggi seperti Ada, bagaimana menata aset C/C++, C#, VB6, MFC, dan COM yang ada untuk diperpanjang umurnya atau dimigrasi juga merupakan tema yang dalam praktik sering berdekatan.
- Pengembangan aplikasi Windows
- Pengembangan komponen COM
- Penggantian aplikasi Windows
- Konsultasi teknis dan tinjauan desain
- Hubungi kami
Referensi
- Ada 2022 Language Reference Manual, Section 12: Generic Units ── Ketentuan unit generik itu sendiri. 12.1 sesuai deklarasi generik, 12.3 instansiasi, 12.4 objek formal (parameter nilai), 12.5 tipe formal (kategori seperti
private,range <>,digits <>), 12.6 subprogram formal, dan 12.7 paket formal. Jika ragu di suatu bab artikel ini, yang paling andal adalah merujuk pasal yang sesuai. - Ada 2022 Language Reference Manual, 2.2: Lexical Elements, Separators, and Delimiters ── Tempat didefinisikannya bahwa
<>disebut compound delimiter «box». Ini dasar notasiboxpada diagram. - GNAT User’s Guide for Native Platforms ── Cara memakai
gnatmakedangnatchop, serta daftar opsi kompilasi termasuk-gnata. Lihat di sini jika terhenti pada langkah bab 3. - Alire Documentation ── Cara memasang
alr, mengelola toolchain (GNAT / gprbuild), dan membuat crate. Untuk membangun lingkungan saja, materi ini sudah cukup. - Kode sampel (GitHub) ── Sampel artikel ini yang sudah dipisah ke berkas mengikuti struktur bab 18.
Artikel terkait
Artikel terbaru dengan tag yang sama untuk mendalami topik-topik terdekat.
Pemrograman sistem real-time dengan Ada — praktik pengendalian prioritas, periode, dan waktu eksekusi
Artikel ini membahas Annex D (sistem real-time) pada Ada melalui delapan contoh kode praktis. Prioritas task, Ceiling_Locking, eksekusi p...
Pemrosesan konkuren yang aman di Ada ── panduan praktis task dan protected object
Artikel pengantar tentang pemrosesan konkuren yang tertanam di bahasa Ada: task dan protected object. Rendezvous (entry/accept), accept s...
Kedalaman virtualisasi Windows (Bagian 3) — Mesin virtual yang boot dalam hitungan detik: mengapa WSL2, Windows Sandbox, dan kontainer begitu ringan
Mengapa WSL2 dan Windows Sandbox start dalam hitungan detik dan terasa begitu ringan? Artikel ini menjelaskan mekanismenya, dari dynamic ...
Kedalaman virtualisasi Windows (Bagian 2) — Memori yang bahkan kernel pun tidak bisa lihat: cara kerja VBS, HVCI, dan Credential Guard
Pada instalasi bersih ke perangkat keras yang kompatibel, VBS diaktifkan secara bawaan dan memakai hypervisor serta SLAT untuk membuat is...
Kedalaman virtualisasi Windows (Bagian 1) — Di mana Windows Anda sebenarnya berjalan? Hypervisor dan partisi
Ketika Anda mengaktifkan Hyper-V, Windows host sendiri berjalan di atas hypervisor sebagai root partition. Artikel ini menjelaskan fondas...
Topik terkait
Halaman-halaman ini menempatkan topik dalam konteks layanan dan keputusan yang lebih luas.
Topik teknis Windows
Portal tentang pengembangan Windows, investigasi bug, dan pemanfaatan aset yang ada.
Pertanyaan yang sering diajukan
Pertanyaan yang sering muncul dalam konsultasi tentang topik artikel ini.
- Apa itu generic di Ada?
- Mekanisme reuse yang menerima tipe, nilai, subprogram, bahkan paket itu sendiri sebagai parameter formal, lalu memeriksa tipenya secara statis pada saat instansiasi dengan new. Bukan sekadar substitusi teks: pada waktu kompilasi ia menetapkan «apakah komponen ini memenuhi kontrak ini». Subprogram generik tidak dapat dipanggil hanya karena sudah dideklarasikan; baru setelah diberi tipe konkret dan diinstansiasi ia menjadi prosedur atau fungsi biasa yang dapat dipakai.
- Apa bedanya generic Ada dengan template C++?
- Ada sejak awal memakai contract model yang menuliskan kontrak secara eksplisit: body generik hanya memakai operasi yang dijanjikan parameter formal, dan body itu sendiri diperiksa tipenya secara mandiri. Template C++ secara historis baru menampilkan error saat instansiasi, dan itu diperbaiki oleh concepts di C++20. Selain parameter nilai dan subprogram, Ada juga dapat menerima paket itu sendiri sebagai parameter formal, sehingga komponen generik dapat dikombinasikan satu sama lain.
- Mengapa kategori tipe ditentukan pada parameter tipe formal Ada?
- Agar operasi yang boleh dipakai di body menjadi kontrak yang eksplisit. Dengan type T is private, yang boleh diasumsikan hanyalah operasi dasar seperti assignment dan perbandingan kesetaraan; perbandingan urutan dan aritmetika tidak dapat dipakai. Jika perlu aritmetika integer, tentukan range <>; jika perlu aritmetika floating-point, tentukan digits <>; jika perlu operasi bit, tentukan mod <>. Penentuan kategori juga berfungsi sebagai spesifikasi tingkat tipe yang memberi tahu pembaca «apa yang dituntut komponen ini».
- Apa yang perlu diperhatikan pada generic Ada?
- Karena instance dibuat per tipe, ukuran kode dapat bertambah jika jumlah instance banyak. Ini trade-off yang juga muncul pada template C++ dan monomorphization di Rust. Selain itu, exception yang dideklarasikan di spesifikasi paket generik menjadi exception yang berbeda per instance. Pedoman praktisnya: mulai dengan parameter formal seminimal mungkin dan tambahkan hanya ketika ada alasan untuk mengganti, serta gunakan named association ketika instansiasi punya banyak argumen.
Profil penulis
Halaman perkenalan penulis artikel.
Go Komura
Direktur KomuraSoft LLC
Berspesialisasi dalam pengembangan perangkat lunak Windows, konsultasi teknis, dan investigasi bug, terutama pada proyek dengan sistem yang sudah ada dan bug yang sulit direproduksi.