9. Jednotka - unit Jednotky jsou základem modulárního programování v pascalu. Mohou sloužit jako knihovny, které lze připojovat k různým programům bez zpřístupnění zdrojového kódu, a také k rozčlenění rozsáhlých programů do logicky souvisejících modulů. Jednotka je samostatným souborem, zdrojovým tvarem jednotky je soubor s příponou .pas, při překladu se z něj vytváří soubor s příponou .tpu. Jednotka může byt použita v hlavním programu nebo jiné jednotce. Poskytne jim vše, co může být uvedeno v části - sekci interface: typy, konstanty, proměnné, procedury a funkce.
Struktura programové jednotky Zdrojový text jednotky je formálně rozdělen do těchto částí: hlavička jednotky, interfejsová část, implementační část a inicializační část. Všechny části jsou povinné a ve zdrojovém textu jednotky musí být obsaženy v uvedeném pořadí. Jednotka dále může obsahovat nepovinné části inicialization a finalization.
Hlavička jednotky Hlavička jednotky začíná klíčovým slovem unit, za kterým následuje identifikátor - název jednotky. Na rozdíl od hlavičky programu nemůže hlavička jednotky obsahovat žádné parametry a je povinná. Podle rezervovaného slova unit totiž kompilátor rozlišuje zdrojový text jednotky od zdrojového textu programu a podle identifikátoru jednotku vyhledává při kompilaci modulů, které deklarují její použití. unit priklad; Uloženo v souboru priklad.pas, po překladu soubor priklad.tpu (turbo pascal unit) v dosu, priklad.dcu (delphi compile unit) ve windows delphi
Sekce interface ("veřejná" část) Tato část jednotky začíná klíčovým slovem interface (a končí před klíčovým slovem implementation). Tato sekce definuje vše, co je viditelné pro kterýkoliv modul (tj. program nebo další jednotku), který tuto jednotku používá, tj. užívající programy či jednotky mají přístup ke všem deklaracím ze sekce interface. Procedury a funkce deklarované v sekci interface jednotky jsou sice viditelné pro kterýkoliv modul, který tuto jednotku používá, ale jejich skutečný kód (tj. implementace) je skryt v sekci implementation. V sekci interface jsou z deklarovaných procedur a funkcí uvedena jen jejich záhlaví, těla jsou uvedena až v sekci implementation. Za rezervovaným slovem interface může následovat případná klauzule uses (jednotka může využívat prostředky jiných jednotek). Klauzule uses se uvádí v případech, kdy jsou veřejné prostředky jednotky deklarovány pomocí (veřejných) prostředků jiných jednotek. Prostředky těchto pomocných jednotek jsou pak přístupné v celém zdrojovém textu uvažované jednotky, nikoli však v dalších modulech, které uvažovanou jednotku používají.
Sekce implementation ("soukromá" část) Tato část jednotky začíná klíčovým slovem implementation. Vše co bylo deklarováno v sekci interface, je v sekci implementation viditelné. Navíc však sekce implementation může zavádět své vlastní deklarace, které pro moduly užívající uvažovanou jednotku nebudou viditelné, tj. užívající modul "neví" o existenci těchto deklarací a nemůže na ně odkazovat, resp. je volat. Přitom ale tyto skryté deklarace mohou být využívány "viditelnými" procedurami a funkcemi uvažované jednotky. Dodatečné deklarace veřejných podprogramů lze v implementační části uvádět v libovolném pořadí a s hlavičkou ve zkráceném tvaru, jako by byly předběžně deklarovány s direktivou forward. Za rezervovaným slovem implementation může opět následovat případná klauzule uses. Prostředky jednotek, uvedených v klauzuli uses implementační části, mohou být využity pouze ve zbytku zdrojového textu uvažované jednotky — v její implementační a inicializační části. Pokud jsou klauzule uses uvedeny současně v interfejsové i v implementační části uvažované jednotky, nesmí obsahovat týž identifikátor (prostředky jednotek, jejichž užití bylo deklarováno již v interfejsové části uvažované jednotky, jsou přístupné i v její části implementační a inicializační).
Inicializační sekce Sekce finalization Je nepovinnou částí jednotky a začíná klíčovým slovem initialization, umožňuje přiřadit počáteční hodnoty proměnným a datovým strukturám, které jednotka sama užívá, resp. je prostřednictvím sekce interface nabízí užívajícím modulům. Inicializační sekce může být použita i k otevření souborů, se kterými se bude později pracovat. Při spuštění programu, který volá nějakou jednotku, je její inicializační sekce volána ještě předtím, než se začne provádět vlastní příkazová část programu. Sekce finalization Je opět nepovinná část, která může následovat po inicializační části. Tato "úklidová" část jednotky se provede při ukončení programu. Příkazová část jednotky je vykonávána pouze jako součást programu, kterým byla jednotka použita. Samostatně jednotku spouštět nelze (podobně jako nelze samostatně spouštět podprogram). Při ladění jednotky je proto vždy zapotřebí použít její prostředky nějakým testovacím programem.
Příklad: V jednotce Matem budou deklarovány typy TVektor a TBod pomocí konstanty Dim (na jednom místě zdrojového tvaru je tedy možno změnit dimenzi příslušného geometrického prostoru). V jednotce bude rovněž deklarována procedura Vektor, která vypočte vektor určený dvěma body, a dále tu bude "jen" formou proměnné deklarován bod O, představující počátek souřadné soustavy (bod O by bylo vhodnější deklarovat jako konstantu příslušného typu). Kromě funkcí pro výpočet požadovaných průměrů bude v implementační části jednotky deklarována funkce PrumerAr pro výpočet aritmetického průměru ze dvou čísel, která bude využita při deklaraci funkcí PrumerKv a PrumerHa.
unit Matem; interface const Dim=3; type TVektor=array[1..Dim] of Real; TBod =array[1..Dim] of Real; var O:TBod; function PrumerGe(A,B:Real):Real; function PrumerKv(A,B:Real):Real; function PrumerHa(A,B:Real):Real; procedure Vektor(A,B:TBod; var AB:TVektor); implementation begin PrumerGe:=Sqrt(A*B); end; function PrumerAr(A,B:Real):Real; PrumerAr:=(A+B)/2;
function PrumerKv(A,B:Real):Real; begin PrumerKv:=Sqrt(PrumerAr(Sqr(A),Sqr(B))); end; function PrumerHa(A,B:Real):Real; PrumerHa:=1/PrumerAr(1/A,1/B); procedure Vektor(A,B:TBod; var AB:TVektor); var I:Byte; for I:=1 to Dim do AB[I]:=B[I]-A[I]; inicialization O[I]:=0; end.
program Test; {$APPTYPE CONSOLE} uses Matem; var X :TBod; OX:TVektor; begin WriteLn(PrumerHa(2,4):10:5); WriteLn(PrumerGe(2,4):10:5); WriteLn(PrumerKv(2,4):10:5); WriteLn(Dim); X[1]:=5; X[2]:=5; X[3]:=1; Vektor(O,X,OX); WriteLn(OX[1]:10:5,OX[2]:10:5,OX[3]:10:5); ReadLn; end.
Kruhové odkazy mezi programovými jednotkami Pokud dvě nebo více jednotek používá navzájem svých prostředků, dochází ke kruhovým odkazům. Nechť například jednotka Prvni volá proceduru Proc2, deklarovanou v jednotce Druha a jednotka Druha proceduru Proc1, deklarovanou v jednotce Prvni. Problém tohoto kruhového odkazu je podobný jako při vzájemné rekurzi podprogramů a spočívá v pořadí kompilace jednotek. Aby mohla být zkompilována jednotka Prvni, musí být již zkompilována jednotka Druha, neboť Prvni používá její prostředky, a naopak. Pokud jsou klauzule uses, tvořící kruhový odkaz, umístěny v interfejsových částech jednotek, dojde při kompilaci kterékoliv z jednotek k chybě — nedovolený kruhový odkaz. Pokud jsou však tyto klauzule umístěny až v implementačních částech jednotek (což je ovšem možné pouze pokud externí prostředky nejsou využívány již v interfejsových částech jednotek), překlad proběhne úspěšně. Kruhové odkazy mezi jednotkami jsou umožněny jejich postupnou kompilací. Před kompilací implementační části jednotky Prvni musí být již zkompilována interfejsová část jednotky Druha a před kompilací implementační části jednotky Druha musí být již zkompilována interfejsová část jednotky Prvni.
Redeklarace a stínění v modulech Bloky programových jednotek jsou hierarchicky nadřazeny bloku modulu (programu nebo jednotky), který je používá. Hierarchicky nejvyšším je blok (automaticky použité) jednotky System, následují bloky ostatních použitých jednotek v pořadí jejich indentifikátorů v klauzuli uses uvažovaného modulu, jehož vlastní blok je vnořen nejhlouběji. Pokud je tedy v modulu redeklarován identifikátor, který náleží některému z veřejných prostředků použitých jednotek, je externí význam identifikátoru zastíněn významem interním (lokálním). Kvalifikovaný identifikátor Přístup k zastíněným prostředkům hierarchicky vyšších bloků je umožněn použitím tzv. kvalifikovaných identifikátorů. Kterýkoliv identifikátor konstanty, proměnné, typu nebo podprogramu lze kvalifikovat předřazením identifikátoru některé z použitých jednotek. Oddělovačem identifikátoru jednotky (vpředu) a identifikátoru jejího prostředku (vzadu) je tečka (uprostřed), podobně jako v přístupu k položkám záznamu.
Závislost modulů Každý modul je přímo závislý na jednotkách, které jsou vyjmenovány v jeho klauzuli (klauzulích) uses a nepřímo závislý na jednotkách, na nichž jsou (přímo nebo nepřímo) závislé vyjmenované jednotky. Při kompilaci kteréhokoliv modulu musí být k dispozici všechny jednotky, na nichž je závislý, ať už přímo či nepřímo. Spojování kódů jednotlivých modulů se totiž provádí až při kompilaci programu. Cílový kód programu (soubor .exe) již obsahuje kód všech svých zdrojových modulů a může být provozován v jejich nepřítomnosti. Je-li dodatečně pozměněn zdrojový text jednotky, je nutno ji rekompilovat. Pokud byla pozměněna interfejsová část jednotky, musí být rekompilovány i všechny jednotky na ní závislé. Pokud se však provedené změny týkaly pouze implementační a inicializační části jednotky, rekompilace závislých jednotek není nutná. Rekompilace na modifikované jednotce závislého programu je samozřejmě nutná vždy.
Standardní programové jednotky Součástí programového balíku Turbo Pascalu je i sada programových jednotek, pro něž používám souhrnné označení „standardní jednotky“. System - standardní prostředky jazyka, používá se vždy, nemusí se uvádět v klauzuli uses Printer - výstup textu na tiskárnu Dos - přístup k rutinám operačního systému Crt - ovládání klávesnice, textových módů obrazovky a tónového generátoru Graph - ovládání grafických módů obrazovky V delphi math, sysutils, ...