1. शुरुआत — “सब कुछ accept करो” नहीं, “क्या promise करते हो”
Statically typed language में reusable code लिखते ही वही समस्या आती है। Integer के लिए लिखा stack string पर भी चाहिए। Floating-point array पर वही statistics चाहिए। Ascending sort की logic descending पर भी चाहिए। Type के हिसाब से वही code copy करते गए तो fix कहीं छूट जाता है। उलटा void* या cast से सब कुछ accept करने लगे तो type safety टूटती है।
Ada का जवाब generic (generic units) है।
Ada का generic mere text substitution नहीं है। Type, value, subprogram, और package को formal parameter के रूप में लेता है, और instantiation के समय statically type-check होता है। Runtime पर “क्या यह type सही है?” नहीं पूछता; compile time पर तय करता है कि “यह component अपने contract को satisfy करता है या नहीं”।
flowchart LR
accTitle: Reuse के तीन रास्ते
accDescr: Copy-paste, void या cast, और Ada generic — तीन reuse तरीकों का फर्क, और Ada generic पर type safety और compile-time check मिलना।
A[Reusable logic] --> B{Reuse कैसे करें}
B --> C[Copy-paste]
B --> D[void* / Object / cast]
B --> E[Ada generic]
C --> C1[Fix कहीं छूट जाना आसान]
D --> D1[Runtime error या type टूटना आसान]
E --> E1[Type-safe]
E --> E2[Compile-time check]
E --> E3[Runtime पर extra dispatch नहीं]
इस लेख में Ada की generic programming को इस क्रम में साफ़ करते हैं।
- Generic subprogram
- Generic package
- Type parameter, value parameter, subprogram parameter
private,range <>,digits <>जैसी type categories- Sort, stack, statistics,
Count_If, key-value store के implementation examples - Formal package parameter से higher-order generic
- Ada का contract model और practical design का तरीका
1.1 यह लेख किनके लिए है, और आप क्या लेकर जाएँगे
यह लेख इन readers के लिए है।
- C++ templates, C# या Java generics, या Rust generics में से किसी का अनुभव हो
- Ada की syntax पर पकड़ कम हो, पर “type पर contract लिखना” वाले design में दिलचस्पी हो
- Long-term maintenance, embedded, या high-integrity काम में reusable component का design सोच रहे हों
Ada syntax के लिए package (specification .ads और body .adb का separation) और in / out / in out parameter modes पता हों तो आगे बढ़ सकते हैं। इन दो पर शक हो तो पहले “Ada language की खूबियाँ” पढ़ने से समझ तेज़ होती है।
जो चीज़ साथ ले जानी है, वह Ada की syntax से ज़्यादा यह design practice है: reusable component क्या माँगता है, उसे implementation से पहले type के रूप में लिख देना। यही सोच C# के interface constraints या C++20 concepts को कितना लिखना है, यह तय करते समय भी सीधे काम आती है।
1.2 पढ़ने का तरीका — पूरा पढ़ना ज़रूरी नहीं
इस लेख में 20 chapters हैं। Cover-to-cover पढ़ना ज़रूरी नहीं; मकसद के हिसाब से इस तरह उठा सकते हैं।
| मकसद | पढ़ें |
|---|---|
| Idea कम समय में पकड़नी हो | chapter 4 (basic model) → chapter 6 (सबसे छोटा generic subprogram) → chapter 13 (contract model) |
| खुद component लिखना हो | chapter 4 → chapter 6 → chapter 7 (generic package) → chapter 8 (behavior inject करना) → chapter 9 (type category) |
| केवल design decisions चाहिए | chapter 13 → chapter 14 (क्या generic बनाएँ) → chapter 15 (common pitfalls) → chapter 17 (checklist) |
| हाथ से चलाना हो | chapter 3 में environment बनाएँ, फिर chapter 6 और 7 के पूरे samples से |
सबसे छोटी route chapter 4, 6 और 13 है। इन तीन में formal parameter, instantiation और contract model — यानी core — पूरा हो जाता है। Chapter 5 और 9 formal parameters की सूची हैं; ज़रूरत पड़ने पर dictionary की तरह खोलें।
यह विषय blog की series “Ada language की खूबियाँ”, “SPARK से formal verification”, “Safe concurrency”, “Real-time systems” के बाद आता है। Ada का “type से design बोलना” वाला तरीका यहाँ generic के नज़रिए से खोलते हैं।
इस लेख का knowledge map
Ada का generic type, value, subprogram और package को formal parameter के रूप में लेता है, और new से instantiation के समय statically type-check होने वाला reuse mechanism है। Generic subprogram और generic package इस mechanism के दो pillars हैं: formal type parameter private, range <> और digits <> जैसी categories से body में इस्तेमाल होने वाली operations का contract साफ़ लिखता है, formal subprogram parameter comparison या predicate जैसे behaviour inject करता है, और formal package parameter पहले से instantiate हो चुके generic package को component के रूप में लेता है। Contract model body को केवल formal parameters की सीमा में अकेले type-check करता है, इसलिए C++ templates में historically instantiation पर पहली बार error दिखने वाली problem Ada में structural रूप से उठती ही नहीं। GNAT और Alire हों तो यह mechanism मुफ्त environment में आज़माया जा सकता है।
flowchart LR
accTitle: Ada generic programming का knowledge map
accDescr: Generic subprogram और generic package type, value, subprogram और package जैसे formal parameters के contract (contract model) पर बने हैं; new से instantiation पर वही contract check होता है; C++ templates में historically जो instantiation-time error problem थी, उससे फर्क दिखाने वाला diagram
ada_generics["Ada generics (generic units)"]
ada_generic_contract_model["Ada generic का contract model"]
ada["Ada (programming language)"]
ada_generic_subprogram["generic subprogram"]
ada_generic_package["generic package"]
ada_generic_formal_type["formal type (Formal Type)"]
ada_generic_formal_subprogram["formal subprogram (Formal Subprogram)"]
ada_generic_formal_package["formal package (Formal Package)"]
ada_generic_instantiation["instantiation"]
ada_generic_formal_object["formal object (value parameter)"]
late_instantiation_error["instantiation के समय ही दिखने वाला template error"]
cpp_templates["C++ templates"]
gnat["GNAT"]
alire["Alire"]
ada -->|"implement करता है"| ada_generics
ada_generics -->|"use करता है"| ada_generic_subprogram
ada_generics -->|"use करता है"| ada_generic_package
ada_generic_subprogram -.->|"require करता है"| ada_generic_formal_type
ada_generics -->|"use करता है"| ada_generic_formal_subprogram
ada_generics -->|"use करता है"| ada_generic_formal_package
ada_generics -->|"require करता है"| ada_generic_instantiation
ada_generic_formal_type -->|"से configure"| ada_generic_instantiation
ada_generic_package -.->|"require करता है"| ada_generic_formal_type
ada_generic_package -.->|"require करता है"| ada_generic_formal_object
ada_generic_formal_subprogram -->|"implement करता है"| ada_generic_contract_model
ada_generic_formal_type -->|"implement करता है"| ada_generic_contract_model
ada_generic_contract_model -->|"prevent करता है"| late_instantiation_error
cpp_templates -.->|"cause हो सकता"| late_instantiation_error
ada_generic_formal_package -->|"require करता है"| ada_generic_instantiation
gnat -->|"implement करता है"| ada
gnat -.->|"से configure"| alire
ada_generic_subprogram -.->|"use करता है"| ada_generic_formal_subprogram
ada_generic_package -.->|"use करता है"| ada_generic_formal_subprogram
ada_generic_instantiation -.->|"require करता है"| ada_generic_formal_object
ada_generic_formal_object -->|"implement करता है"| ada_generic_contract_model
Diagram में solid line हमेशा लागू रहने वाला relation दिखाती है और dashed line conditional relation दिखाती है (शर्तें detail page पर हर relation के explanation में दी गई हैं)। Relations की पूरी list (कुल 21, evidence और certainty सहित) तथा मुख्य concepts की definitions knowledge map की detail page पर संकलित हैं (जापानी में)। Data: JSON-LD / Turtle
2. इस लेख का नक्शा
पहले पूरा नक्शा diagram से पकड़ें। Ada generic को केवल “type argument लेने वाली feature” समझें तो नज़र बहुत संकरी रह जाती है। असल में reuse की इकाई के हिसाब से subprogram, package, subprogram parameter, value parameter और formal package parameter को मिलाते हैं।
mindmap
root((Ada Generics))
Generic Subprogram
Swap
Count_If
Sort
Generic Package
Stack
Statistics
KV Store
Formal Parameters
Type
private
limited private
range box
mod box
digits box
delta box
discrete box
Object
Max_Size
Threshold
Subprogram
Less function
Equals function
Predicate
Package
with package P is new Generic
Design Ideas
Contract Model
Static Checking
Zero-Cost Abstraction
Separate Specification and Body
Diagram में box Ada के <> (box compound delimiter) का नाम है। Mermaid <> को जैसा है वैसा render नहीं कर पाता, इसलिए diagram में ही box लिखा है।
इस लेख को पढ़ना सीधा है। पहले आधे में syntax देखते हैं, बाद में design decisions। Ada पहली बार पढ़ रहे हों तो बारीक syntax याद करने के बजाय यह देखें: “क्या चीज़ formal parameter है” और “उस formal parameter पर कौन-सी operations allow हैं”।
2.1 छोटे शब्दों का dictionary
आगे बार-बार आने वाले शब्दों को पहले English के साथ map कर देते हैं। Ada में इन्हें generic unit, generic subprogram और generic package कहते हैं; अर्थ generic ही है। इस लेख में भी वही English Ada terms रखते हैं।
| इस लेख की notation | English | अर्थ |
|---|---|---|
| generic unit | generic unit | generic से शुरू होने वाला declaration। Generic subprogram और generic package दोनों को साथ बोलने वाला नाम |
| formal parameter | generic formal parameter | generic और declaration body के बीच लिखा, लेने वाला argument। Type, value, subprogram या package |
| formal part | generic formal part | Formal parameters की सूची वाला हिस्सा। “यहाँ contract लिखा जाता है” पढ़ सकते हैं |
| actual parameter | generic actual parameter | Instantiation पर वास्तव में दिया गया type, value, subprogram या package |
| instantiation | instantiation | new से generic unit से ordinary subprogram या package बनाना |
| box | <> |
Ada में <> का नाम। range <> की तरह “concrete type instantiation पर तय होगा” बताता है |
| contract model | contract model | Formal parameters में लिखे promise की सीमा में body लिखना, और body को अकेले type-check करना — Ada का तरीका (chapter 13) |
Body में Ada की notation के हिसाब से range <>, digits <> लिखते हैं, diagrams में ही range box, digits box लिखते हैं। Mermaid <> वाले label को जैसा है वैसा render नहीं करता; अर्थ body जैसा ही है। ऊपर की table के अनुसार Ada स्वयं <> को box कहता है, इसलिए diagram की wording Ada की terminology से बाहर नहीं जाती।
3. Runtime environment और compile कैसे करें
इस लेख का code GNAT 15.x या बाद मानकर है। GNAT Ada का मुख्य compiler है, और Alire से लग सकता है। Alire Ada / SPARK का package manager है; toolchain management और build के लिए भी चलता है।
gnat --version
# GNAT 15.2.1
GNAT Alire (Ada का package manager) से alr install gnat_native gprbuild चलाकर लगाएँ, और PATH में डालें।
इस लेख के samples repository में इस layout से रखे मानें।
flowchart TB
accTitle: Sample files का layout
accDescr: ada-generic-programming directory के अंदर src/snippets और README की file संरचना।
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]
कई compilation units को एक file में समेटे samples को gnatchop से बाँटकर gnatmake करते हैं।
mkdir work
cd work
gnatchop ../src/snippets/01_swap.ada
gnatmake -gnata swap_demo
./swap_demo
-gnata assertions enable करने का option है। Generic इस्तेमाल करने के लिए अनिवार्य नहीं, पर learning samples में contract और boundary conditions जाँचना आसान हो जाता है।
sequenceDiagram
accTitle: gnatchop से run तक
accDescr: एक .ada file को gnatchop से बाँटना, gnatmake से bind और link, फिर executable चलाना।
participant Dev as developer
participant Chop as gnatchop
participant Build as gnatmake
participant Exe as executable
Dev->>Chop: एक .ada file देते हैं
Chop-->>Dev: .ads / .adb / main में बाँटता है
Dev->>Build: gnatmake -gnata main
Build-->>Dev: bind और link तक चलता है
Dev->>Exe: ./main
Exe-->>Dev: run का result
4. Ada generic का basic model
Ada generic को मोटे तौर पर इन तीन steps से सोचना आसान है।
- Generic unit लिखें
genericहिस्से में formal parameters लिखें- इस्तेमाल करने वाला
newसे instantiate करे
flowchart LR
accTitle: Generic का basic flow
accDescr: generic declaration, formal parameters, generic body, new से instantiation, फिर ordinary subprogram या package की तरह इस्तेमाल।
G[generic declaration] --> F[formal parameters]
F --> B[generic body]
B --> I[new से instantiation]
I --> U[ordinary subprogram या package की तरह इस्तेमाल]
F --> F1[type]
F --> F2[value]
F --> F3[subprogram]
F --> F4[package]
उदाहरण के लिए, दो values swap करने वाले logic को generic बनाएँ तो केवल type को formal parameter बना सकते हैं।
generic
type Element is private;
procedure Generic_Swap (A, B : in out Element);
इस बिंदु पर Generic_Swap अभी call नहीं हो सकता। यह “किसी भी Element type पर चलने वाले swap की template” है। Concrete type देने पर ही ordinary procedure बनती है।
procedure Swap_Integer is new Generic_Swap (Integer);
Diagram में संबंध ऐसा है।
flowchart TB
accTitle: Generic_Swap की instantiation
accDescr: एक Generic_Swap template से Integer, Character और record type के लिए अलग-अलग ordinary procedures बनना।
Template[Generic_Swap<br/>type Element is private] -->|Integer देना| SwapInt[Swap_Integer]
Template -->|Character देना| SwapChar[Swap_Character]
Template -->|My_Record देना| SwapRecord[Swap_My_Record]
SwapInt --> ICall[Integer variables swap करना]
SwapChar --> CCall[Character variables swap करना]
SwapRecord --> RCall[My_Record variables swap करना]
महत्वपूर्ण बात यह है कि template body केवल वही operations इस्तेमाल करे जो Element पर available हैं। type Element is private; लिखने पर assignment और equality जैसी basic operations चलती हैं, पर ordering comparison या arithmetic नहीं। यानी generic का declaration स्वयं बताता है कि “यह component क्या assume कर सकता है”।
5. Formal parameter के प्रकार — Ada generic की vocabulary
Ada generic केवल type नहीं लेता। यहीं C# या Java के आम generics से बड़ा फर्क है।
flowchart TB
accTitle: Formal parameters के चार प्रकार
accDescr: Type, object/value, subprogram और package — Ada generic के formal parameters की चार kinds।
P[generic formal parameters] --> T[type parameter]
P --> O[object / value parameter]
P --> S[subprogram parameter]
P --> PKG[package parameter]
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 ...]
Diagram में box Ada के <> (box compound delimiter) का नाम है। Mermaid <> को जैसा है वैसा render नहीं कर पाता, इसलिए diagram में ही box लिखा है।
मुख्य formal parameters table में ऐसे हैं।
| प्रकार | उदाहरण | अर्थ |
|---|---|---|
| type parameter | type Element is private; |
कोई भी definite, non-limited type लेने वाली basic form |
| limited type parameter | type Element is limited private; |
Copy न हो सकने वाले type भी लेना |
| discrete type | type Index is (<>); |
Integer या enumeration जैसे array index में चलने वाले type |
| signed integer type | type Count is range <>; |
+, -, ordering comparison जैसी integer arithmetic assume की जा सकती है |
| modular integer type | type Word is mod <>; |
Bitwise operations या modular integers |
| floating-point type | type Real is digits <>; |
Float, Long_Float, user-defined floating-point type आदि |
| fixed-point type | type Money is delta <>; |
Fixed-point arithmetic |
| value parameter | Max_Size : Positive; |
Size या threshold जैसी चीज़ हर instance पर fix करना |
| subprogram | with function Predicate (...) return Boolean; |
Comparison function या predicate जैसी behavior inject करना |
| package | with package P is new Some_Generic (<>); |
पहले से instantiate हुआ generic package component के रूप में लेना |
यह vocabulary होने से Ada में “सब कुछ accept करो, अंदर खतरनाक काम करो” नहीं, बल्कि “ये operations कर सकने वाले type ही लो” लिखना स्वाभाविक हो जाता है।
6. Generic subprogram — Generic_Swap से सबसे छोटी shape समझें
पहला उदाहरण: किसी भी type के दो variables swap करने वाला Generic_Swap।
6.1 Specification
generic
type Element is private;
procedure Generic_Swap (A, B : in out Element);
generic के बाद वाला हिस्सा formal parameters है। यहाँ Element नाम का type लेते हैं। is private का मतलब: generic body उस type की internal representation नहीं जानती।
इस declaration से दो बातें साफ़ हैं।
Generic_Swapकिसी भीElementtype पर चल सकता है- Body
Elementकी internal structure या ordering comparison पर depend नहीं करती
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 में Element पर केवल assignment है। A < B भी नहीं, A + B भी नहीं। इसलिए Integer, Character, record type, enumeration — assignment चलने वाला type naturally इस्तेमाल हो जाता है।
flowchart LR
accTitle: Swap call से पहले और बाद
accDescr: Temp के जरिए A और B की values interchange होना।
subgraph Before[call से पहले]
A1[A = 10]
B1[B = 20]
end
A1 --> T[Temp = A]
B1 --> A2[A = B]
T --> B2[B = Temp]
subgraph After[call के बाद]
A2[A = 20]
B2[B = 10]
end
6.3 Instantiation
इस्तेमाल करने वाला new लिखता है।
procedure Swap_Int is new Generic_Swap (Integer);
procedure Swap_Char is new Generic_Swap (Character);
अब Swap_Int और Swap_Char ordinary procedures की तरह call हो सकते हैं।
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;
Run का रूप ऐसा है।
Before: X= 10, Y= 20
After : X= 20, Y= 10
यहाँ Swap_Int (X, Y); में Float variable नहीं दे सकते। Swap_Int Integer के लिए instantiate हुई ordinary procedure है। Generic कोई catch-all hole नहीं है जिसमें हर type घुस जाए; हर type के लिए एक type-safe concrete चीज़ बनाने का mechanism है — इस तरह सोचें तो बैठ जाता है।
7. Generic package — type और value को parameter बनाना
एक subprogram नहीं, कई operations और internal state को साथ reuse करना हो तो generic package इस्तेमाल करें। Classic उदाहरण stack है।
Stack में element type और max size बदल दें, तो मूल logic वही रहती है।
flowchart TB
accTitle: Generic_Stack के parameters और instances
accDescr: Element type, Max_Size और operations fix रहें, Integer, Float, Unbounded_String के लिए अलग instances।
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 Specification
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;
यहाँ दो तरह के formal parameters हैं।
Element_Typetype parameter हैMax_Sizevalue parameter है
Max_Size Positive है, इसलिए 0 या उससे छोटी size पर instantiate नहीं कर सकते। Value parameter पर भी type वाली constraint लगा सकते हैं।
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;
इस package body में महत्वपूर्ण बात: Data और Top हर instance पर अलग बनते हैं।
package Int_Stack is new Generic_Stack (Integer, 5);
package Float_Stack is new Generic_Stack (Float, 3);
ये दोनों एक ही template से बनते हैं, पर internal state share नहीं करते।
flowchart LR
accTitle: Int_Stack और Float_Stack अलग state रखते हैं
accDescr: एक Generic_Stack template से दो instances, प्रत्येक की अपनी Top और Data array।
Template[Generic_Stack] --> IntStack[Int_Stack]
Template --> FloatStack[Float_Stack]
subgraph I[Int_Stack की state]
ITop[Top]
IData[Data : Integer array]
end
subgraph F[Float_Stack की state]
FTop[Top]
FData[Data : Float array]
end
IntStack --> I
FloatStack --> F
7.3 Stack की state transition
Stack को state machine की तरह देखना आसान है।
stateDiagram-v2
accTitle: Stack की state machine
accDescr: Empty, NonEmpty, Full के बीच Push और Pop, और overflow/underflow transitions।
[*] --> Empty
Empty --> NonEmpty: Push
NonEmpty --> NonEmpty: Push / Pop
NonEmpty --> Empty: Pop से last element निकालना
NonEmpty --> Full: Push से Max_Size पहुँचना
Full --> NonEmpty: Pop
Full --> Overflow: Push
Empty --> Underflow: Pop
7.4 Usage example
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;
Chapter 3 जैसा ही build करके चलाएँ।
gnatchop ../src/snippets/02_stack.ada
gnatmake -gnata stack_demo
./stack_demo
Size= 3
Pop = 30
Pop = 20
Size= 1
= के दाएँ एक space इसलिए आता है क्योंकि integer type का 'Image non-negative value से पहले एक blank character रखता है। Push तीन बार, Pop दो बार, इसलिए आखिरी Size 1 है। Stack भरा हो (इस example में Max_Size के 5 elements) और फिर Push करें तो Int_Stack.Stack_Overflow, खाली हो और Pop करें तो Int_Stack.Stack_Underflow raise होता है।
Generic package practically “छोटा container”, “fixed-size buffer”, “ring buffer”, “log queue”, “hardware abstraction layer” जैसी जगहों पर अच्छा बैठता है। खासकर Ada में, size को runtime पर vary करने से बेहतर, type या value parameter के रूप में statically fix करना high-integrity systems के साथ मेल खाता है।
8. Formal subprogram parameter — behavior inject करना
केवल type लेने से कुछ चीज़ें अभी भी व्यक्त नहीं होतीं। Sort में element type के अलावा “किसे पहले रखना है” वाली comparison logic भी चाहिए।
Ada में यह comparison function generic का formal parameter बन सकती है।
flowchart LR
accTitle: Sort में comparison function inject करना
accDescr: Generic_Insertion_Sort type, index, array और comparison function लेता है; standard comparison, Greater या custom order।
A[Generic_Insertion_Sort] --> T[Item_Type]
A --> I[Index]
A --> ARR[Item_Array]
A --> CMP[comparison function]
CMP --> ASC[standard comparison इस्तेमाल]
CMP --> DESC[Greater देकर descending]
CMP --> CUSTOM[अपना order देना]
8.1 Specification
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);
यहाँ चार formal parameters हैं।
Item_Type: array element का typeIndex: array index का typeItem_Array: असल array type"<": comparison function
type Index is (<>); discrete type लेता है। Integer ही नहीं, enumeration भी आ सकता है। Array index केवल Positive नहीं, Day जैसी enumeration भी चल सकती है — यही Ada-style है।
with function "<" ... is <>; का is <> मतलब: actual parameter छोड़ दें तो visible standard operator या matching function इस्तेमाल होगी। यानी Integer जैसे type पर पहले से < हो तो comparison function explicit देना ज़रूरी नहीं।
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 बड़े arrays के लिए सही नहीं, पर generic समझाने के लिए सही है। Comparison function बदल दें तो वही loop structure ascending और descending दोनों पर चलती है।
flowchart TB
accTitle: Insertion sort का loop
accDescr: बाएँ से Key निकालना, जरूरत हो तो shift, insert, अंत तक repeat।
Start[Unsorted array] --> Pick[बाएँ से Key निकालना]
Pick --> Compare{Key पिछले element से पहले आता है? }
Compare -->|Yes| Shift[पिछले element को दाएँ सरकाना]
Shift --> Compare
Compare -->|No| Insert[Key insert करना]
Insert --> Done{आखिर तक हो गया?}
Done -->|No| Pick
Done -->|Yes| End[Sorted array]
8.3 एक ही body से ascending और descending
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 standard < इस्तेमाल करता है। Sort_Desc "<" => Greater लिखकर comparison function बदल देता है।
flowchart LR
accTitle: Same data, ascending और descending
accDescr: Sort_Asc standard comparison से, Sort_Desc Greater function से उलटा क्रम।
Data[99, 3, 47, 12] --> A[Sort_Asc<br/>standard comparison]
Data --> D[Sort_Desc<br/>Greater को comparison function देना]
A --> AO[3, 12, 47, 99]
D --> DO[99, 47, 12, 3]
यह mechanism C++ में comparison function object को template argument देने, या Rust में trait bounds से ordering माँगने के करीब है। फर्क यह है कि Ada में formal subprogram parameter के रूप में “इस shape का function दो” साफ़ लिखा जाता है।
8.4 Run example
Chapter 18 के layout के हिसाब से Generic_Insertion_Sort अलग files (generic_insertion_sort.ads / .adb) में हो तो caller ऐसा दिखता है।
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
एक ही Generic_Insertion_Sort body से बनी दो procedures, comparison function बदलने भर से उलटी order में आती हैं। Elements के पहले वाला space integer type के 'Image का है, जो non-negative value से पहले एक blank रखता है।
9. Type category — private से ज़्यादा specific contract लिखना
type T is private; सुविधाजनक है, पर सब कुछ नहीं कर सकता। private type पर arithmetic या ordering comparison naturally assume नहीं की जा सकती। इसलिए Ada formal type parameter पर category specify कर सकता है।
flowchart TB
accTitle: Formal type की categories
accDescr: private, limited private, discrete, range, mod, digits, delta और access — body को कौन-सी operations मिलती हैं।
FormalType[Formal Type] --> Private[private]
FormalType --> Limited[limited private]
FormalType --> Discrete[discrete box: discrete type]
FormalType --> Signed[range box: signed integer]
FormalType --> Modular[mod box: modular integer]
FormalType --> Float[digits box: floating-point]
FormalType --> Fixed[delta box: fixed-point]
FormalType --> Access[access type]
Discrete --> Enum[enumeration]
Discrete --> Int[integer type]
Float --> F1[Float]
Float --> F2[Long_Float]
Float --> F3[user-defined floating-point type]
Diagram में box Ada के <> (box compound delimiter) का नाम है। Mermaid <> को जैसा है वैसा render नहीं कर पाता, इसलिए diagram में ही box लिखा है।
9.1 Category specify करने से क्या फायदा है
Mean या variance निकालने के लिए addition, subtraction, multiplication, division चाहिए। private type पर ये operations assume नहीं की जा सकतीं। इसलिए floating-point type तक सीमित करते हैं।
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;
type Real is digits <>; से साफ़ है कि Real floating-point type है। इसलिए generic body में +, -, *, / चल सकते हैं।
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 Float और 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);
वही statistics अलग precision के floating-point types पर reuse हो जाती है।
flowchart LR
accTitle: Generic_Statistics की कई instances
accDescr: digits box वाले Real से Float, Long_Float और user-defined Real पर Mean/Variance।
Stats[Generic_Statistics<br/>Real is digits box] --> FS[Float_Stats]
Stats --> LS[Long_Stats]
Stats --> MS[My_Real_Stats]
FS --> FCalc[Float पर Mean / Variance]
LS --> LCalc[Long_Float पर Mean / Variance]
MS --> MCalc[user-defined Real पर Mean / Variance]
9.4 Run example
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 standard library है जो Ada.Text_IO.Float_IO को Float पर instantiate करती है। Exp => 0 देने पर exponential form नहीं, साधारण decimal notation आती है। Aft => 3 fractional digits है। ध्यान रहे, यह Variance sample variance नहीं, population variance है (Values'Length से divide)।
9.5 Category specification एक type-level spec है
Category लिखना केवल compiler को चुप कराने की syntax नहीं है। Reader को भी बताती है कि “यह component क्या माँगता है” — यानी एक spec।
| जो logic लिखनी है | सही formal type | वजह |
|---|---|---|
| Swap, store, retrieve | private |
Assignment चलना काफी है |
| Copy न हो सकने वाले resource का management | limited private |
Assignment assume न करें |
| Array index / enumeration states traverse करना | (<>) |
First, Last, Succ, Pred चलते हैं |
| Integer sum, counter | range <> |
Integer arithmetic assume की जा सकती है |
| Bitmask, wrapping counter | mod <> |
Modular arithmetic assume की जा सकती है |
| Mean, variance, numeric computation | digits <> |
Floating-point arithmetic assume की जा सकती है |
| Money, control quantity जैसी fixed precision | delta <> |
Fixed-point arithmetic assume की जा सकती है |
10. Predicate inject करना — Count_If को Ada-style में लिखना
Formal subprogram parameter केवल comparison function नहीं, predicate पर भी चलता है। Predicate वह function है जो value लेकर Boolean लौटाए।
C# का Func<T, bool>, Java का Predicate<T>, C++ का lambda या function object — वही भूमिका Ada में generic के formal subprogram के रूप में लिखी जाती है।
10.1 Specification
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;
यहाँ Predicate पर is <> नहीं लगाया। Standard रूप से visible कोई predicate function नहीं होती, इसलिए caller को हमेशा देना होगा — यही design है।
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;
Flow सीधा है।
flowchart LR
accTitle: Count_If का loop
accDescr: Array के हर element पर Predicate, true हो तो Count बढ़ाना, अंत में Count लौटाना।
Arr[array] --> Loop[हर element traverse करना]
Loop --> P{"Predicate(Item)?"}
P -->|True| Inc[Count बढ़ाना]
P -->|False| Skip[कुछ न करना]
Inc --> Next[अगले element पर]
Skip --> Next
Next --> Result[Count लौटाना]
10.3 Even count और threshold count
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);
एक ही traversal logic से, केवल condition बदलकर दो functions बन जाते हैं।
flowchart TB
accTitle: एक body से दो counters
accDescr: Generic_Count_If से Count_Even और Count_Large, predicate बदलने पर अलग count।
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[even की संख्या]
L --> LR[50 से बड़े की संख्या]
इस example में array traversal, counter का management और result लौटाना सब common है। “क्या गिनना है” केवल function के रूप में inject है। यही Ada में higher-order design की basic shape है।
10.4 Run example
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
Data के 9 elements में even हैं 12, 88, 56, 44, 62 — पाँच; 50 से बड़े हैं 88, 56, 91, 62 — चार।
11. कई parameters को मिलाना — generic key-value store
असली components में एक type parameter अक्सर काफी नहीं होता। Key और value के type, key की comparison, max entries — कई शर्तें मिलानी पड़ती हैं।
यहाँ fixed-size, छोटा key-value store उदाहरण है।
flowchart TB
accTitle: Generic_KV_Store के parameters और इस्तेमाल
accDescr: Key type, value type, equality function, Max_Entries, और Put/Get/Contains से settings store, cache, embedded dictionary।
KV[Generic_KV_Store] --> K[Key_Type]
KV --> V[Value_Type]
KV --> EQ[key equality function]
KV --> M[Max_Entries]
KV --> Ops[Put / Get / Contains]
Ops --> Use1[settings store]
Ops --> Use2[छोटा cache]
Ops --> Use3[embedded के लिए fixed-size dictionary]
11.1 Specification
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;
इस package में चार formal parameters हैं।
| Parameter | प्रकार | भूमिका |
|---|---|---|
Key_Type |
type | Key का type |
Value_Type |
type | Value का type |
"=" |
subprogram | Key की equality |
Max_Entries |
value | Max entries |
Max_Entries पर := 50 default है। कुछ न दें तो 50-entry store बनता है।
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;
यह implementation linear search है, इसलिए बड़े data के लिए नहीं। पर fixed size, छोटा footprint, dynamic allocation नहीं — जहाँ ये गुण मायने रखते हैं, वहाँ चलने वाली shape है।
sequenceDiagram
accTitle: Put और Get का प्रवाह
accDescr: Existing key पर value overwrite, नई key पर free slot, फिर Get से Find_Index के जरिए value लौटाना।
participant App as caller
participant Store as Generic_KV_Store instance
participant Data as Keys/Values/Used
App->>Store: Put(Key, Value)
Store->>Data: Find_Index(Key)
alt existing key है
Store->>Data: Values(Pos) := Value
else नई key
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 Instantiation example
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;
-- existing key पर Put overwrite करता है
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)
Contains के बिना Get करें और key न हो तो Key_Not_Found raise होता है। ऊपर जैसे Contains से branch करें, या exception handler लिखें।
"=" छोड़ा है। Integer पर standard equality operator है, और is <> उसी को उठाता है।
Key case-insensitive string हो तो अपनी equality function दे सकते हैं।
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. Formal package parameter — generic को और component बनाना
Ada generic में package स्वयं formal parameter बन सकता है। इससे “किसी generic package से बना instance” दूसरे generic का input बन जाता है।
flowchart LR
accTitle: Stack instance को logger में देना
accDescr: Generic_Stack से Int_Stack, फिर Generic_Stack_Logger से logged instance।
GS[Generic_Stack] --> IS[Int_Stack]
IS --> Logger[Generic_Stack_Logger]
Logger --> Logged[Int_Stack_Logger_Instance]
12.1 Stack लेने वाला logger
उदाहरण: ऊपर के Generic_Stack का instance लेकर उसका size print करने वाला logger।
generic
with package Stack is new Generic_Stack (<>);
package Generic_Stack_Logger is
procedure Print_Size;
end Generic_Stack_Logger;
Body ऐसी है।
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;
इस्तेमाल करने वाला पहले stack बनाता है, फिर वह stack 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);
इस design से generic components एक-दूसरे से जुड़ जाते हैं।
flowchart TB
accTitle: दो चरणों में generic composition
accDescr: पहले Generic_Stack instantiate, फिर वह instance Generic_Stack_Logger को देना।
subgraph Layer1[पहला चरण]
T1[Generic_Stack] --> I1[Int_Stack]
end
subgraph Layer2[दूसरा चरण]
T2[Generic_Stack_Logger] --> I2[Int_Stack_Logger]
end
I1 --> T2
I2 --> API[Print_Size]
C++ के template template parameters के करीब का इस्तेमाल है, पर Ada में साफ़ लिखा जा सकता है: “इस generic package का instance लो”। बड़े Ada code में container, algorithm, log, check, test helper अलग रखकर मिलाने पर सुविधा होती है।
13. Contract model — Ada generic का सबसे महत्वपूर्ण idea
Ada generic समझने में contract model केंद्र है।
Generic body केवल वही operations इस्तेमाल कर सकती है जो formal parameters ने promise की हों। उदाहरण: केवल type Element is private; लिखा हो तो Element पर < नहीं चल सकता। < चाहिए तो उसे formal subprogram के रूप में लिखें, या type category और specific करें।
flowchart TB
accTitle: Contract model के दो checks
accDescr: Formal part का contract, body उसी सीमा में, body अकेले type-check, instantiation पर actual parameters का check।
Spec[generic formal part<br/>contract] --> Body[generic body<br/>contract की सीमा में implementation]
Body --> Check1[body अकेले type-check]
Spec --> Inst[instantiation]
Actual[actual parameters<br/>actual type, function, value] --> Inst
Inst --> Check2[actual parameters contract satisfy करते हैं?]
Check2 --> Instance[ordinary package/subprogram]
इस design से generic का इस्तेमाल करने वाला ही नहीं, generic लिखने वाला भी सुरक्षित रहता है।
13.1 C++ templates से दिखने वाला फर्क
C++ templates शक्तिशाली हैं, पर historically “template body instantiate होने पर ही error दिखे” वाला व्यवहार रहा। C++20 concepts से सुधार हुआ है, पर Ada generic शुरू से ही contract को साफ़ लिखने वाला model है।
flowchart LR
accTitle: Ada contract model बनाम C++ templates
accDescr: Ada में formal part पर contract, body उसी में check, instantiation पर actual check; C++ में body लिखना फिर instantiation पर requirements concretize, concepts से constraint।
subgraph Ada[Ada]
A1[formal part पर contract लिखना] --> A2[body contract के अंदर check]
A2 --> A3[instantiation पर actual check]
end
subgraph CPP[C++ templates]
C1[template body लिखना] --> C2[instantiation पर requirements concretize]
C2 --> C3[concepts से constraints साफ़ की जा सकती हैं]
end
Java या C# generics में reference types, constraints, type erasure, runtime representation design का केंद्र होते हैं। Ada generic compile time पर concrete instance बनाने की तरफ़ झुकता है।
| नज़रिया | Ada | C++ | Java | Rust |
|---|---|---|---|---|
| Contract कैसे लिखा जाता है | formal part में type, value, function, package | templates / concepts | type parameters और bounds | trait bounds |
| Body का check | formal parameters के contract के अंदर | instantiation पर concretize करना केंद्र | bounds के अंदर | trait bounds के अंदर |
| Runtime cost | static resolution मूल रूप | static generation मूल रूप | type erasure का असर | monomorphization मूल रूप |
| Value parameter | हाँ | हाँ | सीमित | const generics |
| Subprogram को formal parameter बनाना | हाँ | function objects आदि से | lambda / function-type interface | closure / function / trait |
| Package को formal parameter बनाना | हाँ | template templates आदि | नहीं | module structure से अलग |
बारीक language features अलग हैं, पर Ada की विशेषता यह है: contract को syntax के रूप में पहले लिखना।
14. Practical design decisions — क्या generic बनाना चाहिए
Generic सुविधाजनक है, पर सब कुछ generic बना देना सही नहीं। Practically इस तरह तय करें तो चूक कम होती है।
flowchart TB
accTitle: क्या generic बनाना है, कैसे तय करें
accDescr: केवल type अलग, size/threshold अलग, comparison अलग, या internal state/API साथ — हर सवाल का अगला कदम।
Start[Reusable logic है] --> Q1{केवल type अलग है?}
Q1 -->|Yes| GType[type parameter सोचें]
Q1 -->|No| Q2{Size या threshold भी अलग है?}
Q2 -->|Yes| GObject[value parameter जोड़ें]
Q2 -->|No| Q3{Comparison या decision की behavior अलग है?}
Q3 -->|Yes| GSubp[formal subprogram जोड़ें]
Q3 -->|No| Q4{Internal state या API साथ चाहिए?}
Q4 -->|Yes| GPackage[generic package]
Q4 -->|No| Normal[ordinary subprogram काफी है]
14.1 Generic subprogram कहाँ बैठता है
Generic subprogram stateless algorithms के लिए सही है।
SwapSortCount_IfFindMapजैसी transformationMin/Max
Algorithm body छोटी हो, input और output साफ़ हों, तो package से subprogram ज़्यादा पढ़ने योग्य रहता है।
14.2 Generic package कहाँ बैठता है
Generic package वहाँ सही है जहाँ type के साथ कई operations और internal state चाहिए।
- Fixed-size stack
- Ring buffer
- छोटा dictionary
- Statistics suite
- Device-wise I/O abstraction
- Unit-tagged numeric type का operation set
Ada में package specification public API है, package body implementation — यह separation होने से generic package “type-safe module template” बन जाता है।
flowchart LR
accTitle: Spec, body और formal part
accDescr: Formal part spec और body दोनों को contract देता है; spec user को दिखता है, body hidden रहती है।
Spec[package spec<br/>public API] --> User[इस्तेमाल करने वाला]
Body[package body<br/>internal implementation] -.hidden.-> User
Formal[generic formal part<br/>type, value, function का contract] --> Spec
Formal --> Body
14.3 Formal parameters कम से शुरू करें
Formal parameters ज़्यादा हों तो instantiation पढ़ना मुश्किल हो जाता है। शुरू में कम रखें; बदलने की वजह निकलने पर बढ़ाएँ — यही सुरक्षित है।
-- पढ़ने में मुश्किल हो सकता है
package X is new Generic_Foo
(A, B, C, D, E, F, G);
-- named association से intent साफ़ रखें
package X is new Generic_Foo
(Element_Type => Integer,
Index_Type => Positive,
Buffer_Size => 128,
"<" => Less);
Ada में instantiation पर named association चलती है। Generic में design के महत्वपूर्ण फैसले instantiation पर दिखते हैं, इसलिए practical code में named लिखना अक्सर maintain करना आसान रखता है।
15. Common pitfalls
Ada generic शक्तिशाली है, पर पहली बार यहाँ अटकना आसान है।
15.1 private type पर comparison नहीं चलती
ऐसी body नहीं लिख सकते।
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
return A;
else
return B;
end if;
end Bad_Min;
Element केवल private declared है, इसलिए < available होना ज़रूरी नहीं। Comparison चाहिए तो contract में जोड़ें।
generic
type Element is private;
with function "<" (Left, Right : Element) return Boolean is <>;
function Generic_Min (A, B : Element) return Element;
flowchart LR
accTitle: Comparison चाहिए तो contract में लिखें
accDescr: Formal part पर comparison function लिखें तो instantiation पर comparability check होती है; केवल private हो तो body compile error।
Need[body में comparison चाहिए] --> Contract[formal part पर comparison function लिखना]
Contract --> OK[instantiation पर comparability check]
Need --> NoContract[केवल private]
NoContract --> Error[generic body में compile error]
15.2 is <> “सब कुछ auto-infer” नहीं है
is <> सुविधाजनक है, जादू नहीं। Instantiation point पर matching operator या subprogram visible होना चाहिए। अपनी comparison function अलग package में हो तो सही with / use करें, या named association से explicit दें — यही सुरक्षित है।
procedure Sort_By_Age is new Generic_Insertion_Sort
(Item_Type => Person,
Index => Positive,
Item_Array => Person_Array,
"<" => Younger_Than);
15.3 Exception भी instance के हिसाब से अलग होती है
Generic package की specification में exception declare करें तो हर instance पर वह अलग exception बनती है।
package Int_Stack is new Generic_Stack (Integer, 5);
package Float_Stack is new Generic_Stack (Float, 3);
यहाँ Int_Stack.Stack_Overflow और Float_Stack.Stack_Overflow अलग exceptions हैं। एक common exception चाहिए तो generic के बाहर exception define करने वाला design भी सोचें।
flowchart TB
accTitle: हर instance की अपनी exception
accDescr: Generic_Stack की Stack_Overflow declaration से Int_Stack और Float_Stack पर अलग exceptions।
Generic[Generic_Stack<br/>Stack_Overflow declaration] --> I[Int_Stack.Stack_Overflow]
Generic --> F[Float_Stack.Stack_Overflow]
I -.अलग exception.-> F
15.4 Code size बढ़ सकता है
Generic runtime की अतिरिक्त indirection बचाता है, पर हर type का अपना instance बनता है, इसलिए instances ज़्यादा हों तो code size बढ़ सकता है।
यही trade-off C++ templates और Rust monomorphization में भी दिखता है। High-integrity, embedded, real-time के करीब के development में runtime uncertainty काटना होता है, बदले में build-time generated code के size को manage करते हैं।
flowchart LR
accTitle: Instantiation और code size
accDescr: एक generic body से कई type-specific instances, generated code पर runtime type test/boxing कम, instances ज़्यादा हों तो size बढ़े।
Generic[एक generic body] --> I1[Integer वाला]
Generic --> I2[Float वाला]
Generic --> I3[Long_Float वाला]
Generic --> I4[My_Type वाला]
I1 --> Code[generated code]
I2 --> Code
I3 --> Code
I4 --> Code
Code --> Pros[runtime type test या boxing बचाना आसान]
Code --> Cons[instances ज़्यादा हों तो size पर नज़र]
15.5 limited private कब इस्तेमाल करें
type Element is private; assignment assume करता है। File handle, lock, device handle जैसी चीज़ें copy न हों तो limited private सोचें।
generic
type Resource is limited private;
with procedure Close (R : in out Resource);
procedure Generic_Use_And_Close (R : in out Resource);
Copy न हो सकने वाले type पर, value store करने वाले container से बेहतर, procedure apply करने वाला algorithm या reference को explicit रखने वाला design सुरक्षित रहता है।
16. छोटे design patterns
यहाँ practically काम आने वाली shapes संक्षेप में हैं।
16.1 Comparable values पर ही Min देना
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 को comparison चाहिए
accDescr: Element पर comparison function जरूरी, Generic_Min छोटा वाला लौटाता है।
T[Element] --> C[comparison function जरूरी]
C --> M[Generic_Min]
M --> R[छोटा वाला लौटाना]
16.2 Threshold को value parameter बनाना
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;
Value parameter runtime setting के लिए नहीं, instance की property के रूप में fix रखने वाली value के लिए सही है।
16.3 Output का तरीका inject करना
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;
इस shape पर standard output, log, test buffer — destination बदला जा सकता है।
flowchart TB
accTitle: Put को formal subprogram बनाना
accDescr: Generic_Print_Twice Put लेता है; console, log या test buffer पर भेज सकते हैं।
Print[Generic_Print_Twice] --> Put[Put को formal subprogram के रूप में लेना]
Put --> Console[console output]
Put --> Log[log output]
Put --> Test[test buffer]
16.4 Array का index type fix न करें
Ada में array का index type भी महत्वपूर्ण type जानकारी है। Positive पर lock न करें; जरूरत हो तो index type भी formal parameter बनाएँ, 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);
इस design पर Positive index वाले arrays ही नहीं, enumeration index वाले arrays भी चलते हैं।
flowchart LR
accTitle: Index discrete type के उदाहरण
accDescr: Positive range, Day enumeration, State enumeration, अपना integer type — Index इन सब को ले सकता है।
Index[Index is discrete] --> Positive[Positive range]
Index --> Day[Day enumeration]
Index --> State[State enumeration]
Index --> Slot[अपना integer type]
17. Ada-style API के लिए checklist
Generic लिखते समय आखिर में इन बिंदुओं से देखें, पढ़ना आसान रहता है।
flowchart TB
accTitle: Generic design checklist
accDescr: Formal parameters कम, जरूरी operations formal part में, सही category, named association, per-instance state/exception, code size, test instance।
C[Generic design check] --> C1[formal parameters कम हैं?]
C --> C2[जरूरी operations formal part में साफ़ हैं?]
C --> C3[private / range / digits जैसी category सही है?]
C --> C4[named association से पढ़कर instantiate हो सकता है?]
C --> C5[हर instance की state और exception का ख्याल है?]
C --> C6[code size बढ़ना स्वीकार है?]
C --> C7[test के लिए instance तैयार है?]
शब्दों में यही है।
- Body में इस्तेमाल होने वाली operation formal parameter के contract के रूप में दिखनी चाहिए।
privateकाफी हो तोprivateरखें। Arithmetic चाहिए तोrange <>याdigits <>इस्तेमाल करें।- Comparison, hash, output, conversion जैसी type-wise बदलने वाली behavior formal subprogram बनाएँ।
- Size या threshold instance की property हो तो value parameter बनाएँ।
- State हो तो generic package, state न हो तो generic subprogram पहले सोचें।
- Instantiation पर arguments ज़्यादा हों तो named association इस्तेमाल करें।
- Exception और internal state हर instance पर स्वतंत्र हैं — इसी assumption पर design करें।
18. Sample की पूरी file layout
लेख के samples को files में बाँटें तो यह layout पढ़ने में आसान रहता है।
flowchart TB
accTitle: Sample repository का file layout
accDescr: generics में ads/adb specs, demos में callers — Generic_Swap से KV store तक।
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]
छोटे article samples के लिए एक file में रखकर gnatchop चलाना सुविधाजनक है, पर practical काम और long-term maintenance सोचें तो specification .ads और body .adb अलग रखना Ada-style layout है।
19. सार — type से reuse की सीमा तय करना
Ada की generic programming केवल “type-independent code लिखने की feature” नहीं है। असल बात यह है: reusable component क्या माँगता है, उसे type, subprogram और value के contract के रूप में साफ़ लिखना।
flowchart LR
accTitle: Contract से reuse तक
accDescr: Contract लिखना, generic body, type/value/function देकर instantiate, type-safe इस्तेमाल, बिना copy reuse।
Contract[contract लिखना] --> Generic[generic body लिखना]
Generic --> Instance[type, value, function देकर instantiate]
Instance --> Safe[type-safe इस्तेमाल]
Safe --> Reuse[बिना copy reuse]
इस लेख में देखा, Ada generic इन चीज़ों को formal parameter बना सकता है।
- Type
- Value
- Subprogram
- Package
Type पर private, limited private, range <>, mod <>, digits <>, delta <>, (<>) जैसी काफी बारीक categories भी लिखी जा सकती हैं। इससे generic body “पता नहीं चलेगी ऐसी operation” पर depend नहीं करती; contract में लिखी operations से सुरक्षित implementation होती है।
C या पुराने C++ assets में reuse के लिए macro, void*, function pointer, हाथ से लिखा type branching दिखता है। Ada generic उन इस्तेमालों में से बहुत को type-safe, पढ़ने योग्य रूप में बदल सकता है। खासकर long-term maintenance, embedded, real-time, high-integrity software में यह “compile time पर सीमा तय करने वाला design” बड़ा मूल्य रखता है।
20. संबंधित consulting क्षेत्र
KomuraSoft LLC Windows app development, existing assets की investigation और modification, COM / ActiveX / 32-bit / 64-bit की सीमा साफ़ करना, technical consulting और design review करता है। Ada जैसी statically typed, high-integrity के करीब की design ही नहीं — मौजूदा C/C++, C#, VB6, MFC, COM assets को कैसे साफ़ करके चलते रखना या migrate करना है, यह practically पास का विषय रहता है।
- Windows app development
- COM component development
- Windows app replacement
- Technical consulting / design review
- Contact
संदर्भ
- Ada 2022 Language Reference Manual, Section 12: Generic Units — generic units का specification स्वयं। 12.1 generic declarations, 12.3 instantiation, 12.4 formal objects (value parameters), 12.5 formal types (
private,range <>,digits <>जैसी categories), 12.6 formal subprograms, 12.7 formal packages। लेख के किसी chapter पर अटकें तो वही clause खोलें। - Ada 2022 Language Reference Manual, 2.2: Lexical Elements, Separators, and Delimiters —
<>को compound delimiter “box” कहा गया है। Diagram मेंboxलिखने का आधार यही है। - GNAT User’s Guide for Native Platforms —
gnatmake,gnatchopका इस्तेमाल और-gnataसमेत compile options। Chapter 3 की procedure अटके तो यहाँ देखें। - Alire Documentation —
alrलगाना, toolchain (GNAT / gprbuild) manage करना, crate बनाना। केवल environment setup हो तो यही काफी है। - Sample code (GitHub) — इस लेख के samples, chapter 18 के layout में files में बाँटे हुए।
संबंधित लेख
निकटवर्ती विषयों में गहराई से जाने के लिए समान टैग वाले नवीनतम लेख।
Ada में सुरक्षित concurrency — task और protected object की practical guide
Ada के language-embedded concurrency — task और protected object — का introductory लेख। rendezvous (entry/accept), selective accept, prote...
Ada में real-time systems programming — priority, period और execution time control
Ada के Annex D (Real-Time Systems) को 8 practical code examples से सीखें। Task priority, Ceiling_Locking, delay until से periodic executi...
Windows Virtualization Internals (भाग 3) — सेकंडों में boot होने वाली VMs: WSL2, Windows Sandbox और containers इतने हल्के क्यों हैं
WSL2 और Windows Sandbox सेकंडों में start होकर इतने हल्के क्यों लगते हैं? यह लेख dynamic base image और direct map से dynamic memory alloc...
Windows Virtualization Internals (भाग 2) — वो मेमोरी जिसे कर्नेल भी नहीं देख सकता: VBS, HVCI और Credential Guard कैसे काम करते हैं
Compatible hardware पर clean install में VBS default से enable होता है और hypervisor तथा SLAT से kernel से मज़बूत isolation बनाता है। यह ...
Windows virtualization की गहराई (भाग 1) — आपका Windows वास्तव में कहाँ चल रहा है? Hypervisor और partitions
जब आप Hyper-V enable करते हैं, तो host Windows खुद root partition के रूप में hypervisor के ऊपर चलता है। यह लेख VT-x, SLAT और VMBus की भूम...
संबंधित विषय
ये पृष्ठ विषय को सेवाओं और निर्णयों के व्यापक संदर्भ में रखते हैं।
Windows के तकनीकी विषय
Windows विकास, बग जाँच और मौजूदा संपत्तियों के उपयोग का प्रवेश-द्वार।
अक्सर पूछे जाने वाले प्रश्न
इस लेख के विषय पर परामर्श में अक्सर पूछे जाने वाले प्रश्न।
- Ada में generic क्या है?
- Type, value, subprogram और package को formal parameter के रूप में लेकर, new से instantiation के समय statically type-check होने वाला reuse mechanism है। यह mere text substitution नहीं है; compile time पर तय होता है कि यह component अपने contract को satisfy करता है या नहीं। Generic subprogram को केवल declare करने से call नहीं कर सकते। Concrete type देकर instantiate करने के बाद ही वह ordinary procedure या function बनता है।
- Ada generics और C++ templates में क्या फर्क है?
- Ada शुरू से ही contract को साफ़ लिखने वाला contract model इस्तेमाल करता है। Generic body केवल वही operations इस्तेमाल करती है जो formal parameters ने promise किए हों, और body अकेले type-check होती है। C++ templates में historically error अक्सर instantiation के समय ही दिखता था; C++20 concepts से यह बेहतर हुआ। Ada में value parameter और subprogram parameter के अलावा package को भी formal parameter बना सकते हैं, इसलिए generic components को एक-दूसरे से compose किया जा सकता है।
- Ada के formal type parameter पर type category क्यों specify करते हैं?
- Body में कौन-सी operations मानकर चल सकते हैं, यह contract के रूप में साफ़ करने के लिए। type T is private पर assignment और equality जैसी basic operations ही assume की जा सकती हैं; ordering comparison या arithmetic नहीं चलती। Integer arithmetic चाहिए तो range <>, floating-point arithmetic चाहिए तो digits <>, bitwise operations चाहिए तो mod <> जैसी category लिखें। Category specification reader के लिए type-level spec भी है: यह component क्या माँगता है।
- Ada generics में किन बातों का ध्यान रखना चाहिए?
- हर type के लिए अलग instance बनता है, इसलिए instances ज़्यादा हों तो code size बढ़ सकता है। यही trade-off C++ templates और Rust monomorphization में भी दिखता है। Generic package की specification में declared exception हर instance पर अलग exception बनती है। Formal parameters शुरू में कम रखें, बदलने की वजह निकलने पर बढ़ाएँ, और arguments ज़्यादा हों तो instantiation पर named association इस्तेमाल करें — यही practical guideline है।