A dokumentum az Ada-83 nyelvi sajátosságaival foglalkozik. Az Ada-nak azonban létezik egy újabb, Ada-95 nevû szabványa, amely sok területen kiterjesztette, a modern programozás iényeihez alakította az Ada-83 eszköztárát. Éppen ezért tanulságos lehet összevetni az egyes fejezetek kapcsán, hogy mi a különbség a két nyelv között. Erre egyes helyeken explicit is felhívtam a figyelmet, ám ezeken túl is lehetnek eltérések, melyek érdemesek a figyelemre.
Tartalom:
Az Ada-83 változói és kifejezései megfelelnek
a Pascal-típusú nyelvekben megszokottnak. A használni
kívánt azonosítókat használatuk elõtt
deklarálni kell. A deklaráció változók
illetve konstansok deklarációjában tartalmazhatja azok
definícióját (az utóbbiaknál ez
értelemszerûen kötelezõ is). Az ilyen
definíciók kiértékelése futási
idõben történik, így szerepelhet benne
függvényhívás is.
Néhány érdekesség a nyelvben:
Az egyedeket átnevezve újabb névvel láthatjuk el. Így megadhatunk nekik az aktuális kontextusba jobban illeszkedõ elnevezést, mely által a programunk olvashatóbbá válik.
A változóinkhoz és típusainkhoz az Ada úgynevezett attributumokat rendel, melyek segítségével lekérdezhetjük azok tulajdonságait. Ezeket az attributumokat használhatjuk kifejezésekben is. Ilyenek például: egy tömb indexhatárai, egy változó típusa stb.
Logikai kifejezéseknél a megszokott and és or mellé bevezették az and then illetve or else mûveleteket, mivel más nyelvek általában nem megfelelõen kezelték az ilyen kifejezések kiértékelésének problémáját, nevezetesen, hogy ki kell e értékelni azokat az operandusokat, amelyek már nem változtathatják meg a kifejezés értékét. Mivel esetenként az ilyen kiértékeléseknek lehet mellékhatása, így a fenti megkülönböztetés hasznos lehet abban, hogy pontosan elõírhassuk vele szándékainkat.
Az Ada-83 még alapvetõen procedurális
vezérlésû, de a taskok és azon belül is az
entry-hívások használata mutat némi
hasonlóságot az eseményvezérelt
szemlélettel.
Vezérlési szerkezetek:
Szekvencia
Az Ada-ban az egymás után következõ
utasításokat ';'-vel kell elválasztani.
Elágazás
Az Ada-ban kétféle elágazástípus van:
If-es elágazás:
Az if-es elágazás lehet egy-, kettõ-, illetve több
ágú, vagyis egy if utasításnak lehet
tetszõleges számú elsif ága és
legfeljebb egy else ága. A "csellengõ" else-ek
problémájának megoldására az if
utasítást egy endif kulcsszóval kell lezárni.
Az egyes ágak feltételeinek kiértékelése
felülrõl lefelé történik, így az
alábbi két if utasítás ekvivalens:
if C1 then S1 |
if C1 then S1 |
Case-es elágazás
Az elágazást vezérlõ kifejezés
értéke diszkrét típushoz kell, hogy tartozzon.
Az ágaknak minden lehetséges értéket le kell
fedniük, egy érték két ágban nem szerepelhet.
A teljes lefedéshez használhatunk az others ágat,
melynek azonban az ágak között utolsóként
kell szerepelnie. A case-szerkezetet egy end case
utasítás zárja, a program futása a kiválasztott
ág végrehajtása után innen fog folytatódni
(ellentétben például a C nyelv switch
szerkezetével).
Az egyes ágaknál megadhatunk konkrét értékeket,
de akár intervallumokat is. Az ágakat a when
kulcsszó vezeti be, majd a lehetséges értékek
felsorolása következik '|' jellel elválasztva,
végül egy '=>' jel után a végrehatandó
utasítások. Az ág végét a
következõ ág kezdete vagy a case-szerkezet vége
jelzi.
Ciklusok
Az Ada nyelv háromféle cikluskonstrukciót nyújt
a programozóknak. Ezek a következõk:
While-ciklus
Ez a ciklusfajta a megszokott elöltesztelõ ciklus, a
ciklusfeltételt mindíg a ciklusmag végrehajtása
elõtt értékeli ki és a ciklusmagot csak a
feltétel igaz értéke esetén hajtja végre.
Formája:
while Condition loop S end loop;
For-ciklus
Ez a ciklusfajta a szokásos iteráció egy
ciklusváltozó segítségével. A
ciklusváltozó sorban felveszi egy diszkrét intervallum
elemeit és mindegyik értékre végrehajtja a
ciklusmagot. Formája:
for identifier in [reverse]
discrete_range loop S end loop;
Specialitások:
A reverse kulcsszó hatására a ciklusváltozó az intervallum elemeit fordított sorrendben veszi fel.
Üres intervallum esetén a ciklusmag egyszer sem hajtódik végre.
A megadott ciklusváltozó a ciklus kezdetekor létrejön és a ciklus befejeztekor megszûnik.
A ciklusváltozó a ciklusmagra nézve lokális konstans (azaz a ciklusmagban megváltoztatni nem lehet). A cikluson kívül definiált azonos nevû változókat eltakarja
A ciklusváltozó az intervallum minden értékét felveszi. Értéket kihagyni nem lehet.
Végtelen ciklus
A ciklus a ciklusmagot végtelen sokszor ismétli.
Formája:
loop S end loop;
Mindhárom ciklusfajtánál szerepelhet a ciklusban exit
utasítás, melynek segítségével
kiléphetünk a ciklusból. Ilyenkor a program
végrehajtása a ciklushoz tartozó end loop-tól
folytatódik. A ciklusok elláthatóak azonosítóval
is. Az azonosítót a ciklust bevezetõ kulcsszó
elé kell írni, majd kettõsponttal lezárni,
továbbá ugyanazt az azonosítót a ciklushoz
tartozó end loop után is ki kell írni. Ha az
exit utasításban megadjuk a ciklus
azonosítóját, akkor az exit arra a ciklusra fog
vonatkozni. Így lehetõség van egyszerre több
ciklusból való kilépésre (egymásba
ágyazott ciklusok esetén).
Goto
Az Ada-ban van goto utasítás. Bár feltétlen
szükség nincs rá, hatékonysági okoból
néha szükség lehet rá. Formája:
goto label;
Blokk utasítás
Az Ada lehetõséget kínál
blokkutasítás használatára.
Segítségével könnyen hozhatunk létre rövid
élettartamu változókat, amelyekre a programnak csak
egy rövid szakaszán van szükségünk. Másik
elõnye, hogy a blokknak saját kivételkezelõ
része lehet (lásd kivételek). Formája:
declare declarative_part begin S end;
Az egymásba ágyazott blokkokban az azonos azonosítók
eltakarják egymást. Hogy elérhessük egy
külsõ blokk azonos nevû azonosítóját,
lehetõség van a blokkok elnevezésére, a ciklusokhoz
hasonló módon. Ekkor a külsõ blokk azonos nevû
változóját minõsített hivatkozással
(blokknév.azonosító) érhetjük el.
Az Ada-ban lehetõség van alprogramként fügvények
és eljárások létrehozására is.
Alprogram hívásakor a formális és aktuális
paraméterek összerendelése történhet
pozíció, név vagy mindkettõ szerint. A név
szerinti hozzárendelés formája:
fomális_paraméter =>
aktuális_paraméter
Az Ada háromféle paraméterátadási módot
ismer:
Érték szerinti (in): ez a default mód. Függvénynek csak ilyen paramétere lehet. A formális paraméter az alprogram törzsére nézve konstans (csak olvasható). Aktuális paraméter ilyenkor tetszõleges kifejezés lehet.
Eredmény szerinti (out): Ilyenkor a formális paraméter az alprogramon belül csak írható. Az aktuális paraméter csak balérték lehet, értéke a visszatéréskor felveszi a formális paraméter értékét.
Érték - eredmény szerinti (in out): Az aktuális paraméterrõl a híváskor másolat készül, amelynek értéke az alprogram futása alatt független az aktuális paraméter értékétõl. Visszatéréskor azonban az felveszi a formális paraméter értékét. Az alprogramban a formális paraméter írható olvasható. Aktuális paraméter ilkyenkor is csak balérték lehet.
További tulajdonságok:
Az alpogramok paraméterszáma kötött. Változó paraméterszámú alprogramot nem lehet írni.
Az alprogramok külön is fordíthatóak, a hívónak ilyenkor meg kell neveznie elõre a hívni kívánt alprogramot a with kulcsszóval.
A paramétereknek adhatóak default értékek, így az alprogramok hívhatóak kevesebb paraméterrel is. A paraméterek a paraméterlista végérõl egyszerûen elhagyhatóak, ha a közepérõl is szeretnénk elhagyni, úgy a tovbábbi paramétereket explicit (név szerint) hozzá kell rendelni a formális paraméterekhez.
Az in paraméterek az alprogramra nézve konstansok, hasonlóan a C/C++ const paramétereihez.
Paraméterként átadható tetszõleges összetett típus is. Visszatérési érték is bármi lehet, kivéve limited private típusokat (lásd Típusok).
Az alprogramok rekurzív hívása megengedett.
Az alprogram azonosítók túlterhelhetõek (az operátorok is, bár free operátor nincs). A túlterhelés mindaddig megtehetõ, amíg valami különbeség van a két változat paraméterlistájában (azaz a függvények visszatérési értéke alapján nem lehet túlterhelni).
Lehetõség van az alprogramok átnevezésére, ám törzsüket átdefiniálni nem lehet.
Az alprogramok nem önálló típusok, alprogramra mutató referencia nem adható meg, alprogram nem adható át paraméterként. (Helyette lásd Generic).
Az Ada a programok megbízhatóságának növelése céljából igen erõsen típusos. Automatikus típuskonverzió csak nagyon ritka esetben van (lásd altípusképzés). Erõteljesen támogatja viszont az új típusok létrehozását, származtatását (lásd private és limited private típusok).
Az Ada típusosztályai:
Az Ada egyik specialitása a tíõusokhoz rendelt úgynevezett attributumok. Ezek azon típusosztálynak müveletei, amelybe az adott típus tartozik. Ezen attributumok egy része az adott osztály típusain, mások az adott osztály valamely típusába tartozó tetszõleges objektumon, megint mások egyszerre mindkettõn értelmezettek. Ilyen attributumok a következõk (T - típus, V - egy objektum):
E két attributum bármely típusra értelmezve van.
1.) Felsorolási típusok
Az elemeinek felsorolásával adjuk meg. A felsorolás
sorrendje automatikusan egy rendezést határoz meg a
típusértékeken (ezért a <, <=, >=, >,
in, not in operátorok automatikusan
definiálódnak hozzájuk). Ada specialitás, hogy
a típus literáljai túlterhelhetõek, azaz egyazon
literál szerepelhet több felsorolási típus
elemeként is. Ilyenkor, ha nem egyértelmû, hogy mely
típusértékrõl van szó, minõsített
kifejezéssel hivatkozhatunk rá. Ennek formája:
T'(literál)
2.) Egész típusok
Az Ada az INTEGER, SHORT_INTEGER, LONG_INTEGER egész típusokat
tartalmazza. Eznkívül két predefinit altípusát
ismeri az INTEGER típusnak, a NATURAL illetve a POSITIVE
altípusokat.
A diszkrét típusokon értelmezett attributumok: PRED,
SUCC, POS, VAL, FIRST, LAST, IMAGE, VALUE, WIDTH
Ezek közül az utóbbi hárhat emelném ki. Az
IMAGE és a VALUE attributumok a T-->String illetve a String -->
T típuskonverzió eszközei, a WIDTH pedig azt mondja meg,
hogy a típus elemei milyen hosszú stringekké
konvertálódnak.
Az Ada, a számábrázolási pontatlanságok
miatt, a hordozhatóság megörzése érdekében
új fogalmakat vezet be. Minden valós típushoz
definiálja azon típusértékek halmazát,
amit minden implementációnak tudnia kell ábrázolni.
Ezeket nevezzük az adott típus modellszámainak.
Ezenkívül minden valós típushoz definiálja
a biztonságos szám fogalmát, amely az adott
implementációban pontosan ábrázolható.
Nyilván minden modellszám bitonságos kell, hogy
legyen.
Minden valós számhoz létezik egy legszûkebb, õt
tartalmazó, modellszámokkal határolt intevallum. Ez
az intervallum fogja reprezentálni az adott számot a
késõbbiekben, ugyanis a típus mûveletei ilyen
intervallumokon vannak definiálva.
1.) Lebegõpontos típusok:
Egy predefinit lebegõpontos típus van az Ada-ban, a FLOAT,
ám mivel ennek ábrázolása
implementációfüggõ, a programokban nem ezt, hanem
a belõle származtatott típusokat használják.
A típusszármaztatáskor megadható a
számábrázolási pontosság (így
tehetõ implementációfüggetlenné a program)
a digits kulcsszó után.
Attributumok: DIGITS, MANTISSA, EPSILON, SMALL, LARGE, EMAX
2.) Fixpontos típusok:
Nincs predefinit fixpontos típus, a programozó
definiálhat ilyeneket az ábrázolási pontosság
(delta) megadásával.
Attributumok: DELTA, MANTISSA, SMALL, LARGE
Az Ada-ban megengedett tetszõleges dimenziószámú
tömbök létrehozása. Az indexek tetszõleges
diszkrét típus elemei lehetnek, és tömbelem is
lehet bármilyen típusú (még taszk is).
Lehetõség van továbbá, határozatlan
indexhatárú tömbtípus létrehozására.
Ilyen típus esetén a tömb objektum deklarációjakor
meg kell adnunk egy diszkrét intervallumot.
Az indexhatárok a tömbtípus attributumaival
kérdezhetõek le:
T'RANGE(n) megadja, hogy az n. index milyen intervallumból veszi az értékeit
Azonos típusú tömbök között az értékadás megengedett, amennyiben az elemtípuson van értelmezve értékadás (azaz nem limited private típusú). Kezdõértéket is adhatunk a tömbnek aggregált formában, zárójelek között felsorolva az elemeket. Ilyenkor az elemek elhelyezése pozíció szerint (sorfolytonosan) történik, ám lehetõség van index=>érték formában konkrét pozícióra is elemet helyezni, illetve utolsóként megadható others ág is. Az index helyén megadhatunk egyetlen értéket, néhány értéket '|' jellel elválasztva, vagy akár intervallumot is.
Az egydimenziós tömbökre (vektorokra) az Ada még további lehetõségeket is kínál. Vektoroknak hivatkozni lehet résztömbjére és azonos hosszúságú, azonos bázistípusú résztömbök esetén, ha a bázistípuson értelmezett az értékadás akkor résztömbökön is elvégezhetõ az értékadás. Fontos megjegyezni, hogy az egy elemû (rész)tömb nem azonos típusú egyetlen tõmbelemmel, így nem is adható értékül egyik a másiknak.
További tömbmûveletek:
Az Ada rekordtípusának mezõire minõsített
jelöléssel
(rekordazonosító.mezõnév)
hivatkozhatunk. Nincs a Pascal with-jéhez hasonló
utasítás. Az egész rekordra egyszerre a .all
minõsítõssel hivatkozhatunk. Ennek jelentõssége
a rekordra vonatkozó access típusoknál van, mivel
az Ada ahol lehet nem tesz különbséget a rekordtípus
és a pointere között, ám teljes rekordok
értéküladásánál két pointer
esetén jelölni kell, hogy nem a címek, hanem a
memóriatartalmak másolását kívánjuk
elérni.
A rekordok korlátlanul egymásba ágyazhatóak és
a mezõk típusára sincs semmilyen korlátozás.
A rekordnak, a tömbhöz hasonlóan,
agregált formában is lehet értéket adni. Az
értékek és a mezõk összerendelése
történhet pozíció szerint, vagy a mezõnév
megadásával, közvetlenül. A rekord az egyetlen
típus amelynek a típus definiálásakor lehet (default)
kezdõértéket adni.
Az Ada rekodjainak adható paraméter, úgynevezett
diszkrimináns, amely a rekordnak mezõje lesz,
értékét a rekordobjektum létrehozásakor
meg kell adni, kivéve ha van default érték megadva.
A diszkrimináns nem kezelhetõ teljesen azonos módon
a többi rekordmezõvel, mivel értéket csak az
egész rekorddal együtt kaphat. Egy rekordnak több
diszkriminánsa is lehet, ám ha egynek adtunk default
értéket, akkor mindegyiknek kell. A diszkrimináns csak
diszkrét típusú lehet. Fontos megjegyezni, hogy ha a
diszkriminánsos rekord létrehozásánál
explicit megadtuk a diszkrimináns értékét, akkor
ezzel a típus egy altípusát állítottuk
elõ, ám ha a default értéket használtuk
fel akkor a típust nem szûkítjük le, a maximális
méretû memória foglalódik le. A diszkrimináns
értéke csak ez utóbbi esetben változtatható
meg.
A diszkrimináns segítségével
valósítható meg az únió típushoz
hasonló, variáns rekord is. A rekord variáns része
ugyanis csak a diszkriminánstól függhet. A diszkrimináns
értékeihez tartozó eseteket a when kulcsszó
vezeti be. Adhatunk meg intervallumot is, de az nem lehet üres. Az
ágaknak az összes lehetséges értéket le
kell fedniük.
IV. Access típus
Az Ada a pointer típus helyett vezette be annak biztonságosabb
változatát az access típust. Az access
típusú vltozóknak default értéke a
null, a sehová sem mutató pointer. Az Ada-83-ban nincs
címképzõ operátor. A statikus obkjetumaink címe
nem kérdezhetõ le, így access típusú
változó csak allokátorhívással, vagy
másik ilyen típusú változótól kaphat
értéket. Általában nincs szükség
a dereferencia jelölésére sem, a
szövegkörnyezetbõl eldönthetõ, hogy a
memóriacímre vagy az adott címen található
objktumra történik hivatkozás. Ha ez mégsem
egyértelmû, akkor a
változónév.all jelöléssel
explicit megmondhatjuk, hogy most a mutatott objektumra hivatkozunk.
Az Ada-ban nincs memóriafelszabadító utasítás.
A nem használt memóriaterületek
felszabadításáról a futtató környezet
gondoskodik.
Az access típuson nincs a C-hez hasonló pointer aritmetika
értelmezve.
Az Ada számos eszközzel támogatja a saját típusok készítését. Az enkapszuláció eszközei a private és limited private típusok illetve a package-ek, melyek segítségével különbözõ láthatósági területeket hozhatunk létre. Nem tesz különbséget a saját illetve a beépített típusok között. Konstruktotok írására nincs lehetõség, bár ilyen célra részben felhasználható a rekordmezõkhöz default értékek rendelése.
A generic az általánosság növelésének eszköze, a makróutasítások továbbfejlesztése. Segítségével olyan kódrészletek írhatóak amelyek más kódrészletekkel paarméterezhetõek. A makrók továbbfejlesztései, ugyanis azok (lásd C) nem végeznek semmilyen ellenõrzést a makró "példámyosítása"-kor, azaz meghívásakor, míg a generic-ek esetén történik több-kevesebb ellenõrzés. Ez persze együtt jár bizonyos korlátozásokkal is (például, hogy mi lehet generic paramétere, mibõl készíthetõ egyálatalán generic, stb.). Az Ada-83, filozõfiájának megfelelõen, a biztonságosság határain belül, igyekszik a programozónak a lehetõ legnagyobb szabadságot nyújtani.
Az Ada-83 generic-jének tulajdonságai:
Ada-ban generic lehet package vagy alprogram, azaz minden, ami önállóan fordítható. Persze a fordítás ilyenkor lényegében csak a szintaktikus ellenõrzésre terjed ki.
Generic paramétere lehet:
Konstans: Itt tetszõleges konkrét objektumot vagy adatot átadhatunk.
Típus: Itt megköthetõ a típusosztály is, így a típusosztályra jellemzõ attributumok szabadon használhatóak a generic-ben annélkül, hogy explicit átadtuk volna õket paraméterként. Ez alól két kivétel van, a diszkrét típusok 'IMAGE és 'VALUE attributumai. Egyéb tekintetben tetszõleges típus lehet paraméter.
Alprogram: Itt adhatóak át a további mûveletek, amelyek szükségesek az átadott típusokhoz. Lehetõség van default alprogramok átadására is. Ez azt jelenti, hogy pl. megadjuk, hogy van egy default, "+"-szal jelölt összeadás mûvelet értelmezve az egyik típuson. Ekkor, ha egy olyan típussal példányosítunk, amelynek van ilyen mûvelete, és nem adunk meg explicit egy másikat, akkor a fordító automatikusan az adott típushoz tartozó "+" mûvelettel végzi el a példányosítást.
Nem lehet viszont paraméter egy másik generic, azaz nem köthetjük ki, hogy csak egy bizonyos másik generic valamely példányával lehessen példányosítani. (Vö. Ada-95)
A példányosításkor a fordító ellenõrzi, hogy
Az átadott adatok és objektumok a kívánt típusúak-e?
Az átadott típusok beletartoznak-e a megadott típusosztályba?
A átadott alprogramok megfelelnek-e a megadott specifikációnak (egyezik-e a szignatúrájuk).
Nem ellenõrzi azonban, hogy az átadott alprogramok ténylegesen azt a mûveletet valósítják meg, amire mi akarjuk használni. Ez lehet gyakran lehet elõnyös, ha például egy rendezés irányát úgy befolyásolhatjuk, hogy "<" helyett ">"-bal példányosítjuk az általános rendezõ generic-ünket, ugyanakkor okozhat bajt is, ha nem a megfelelõ eljárással példányosítunk és azt, ennek következtében nem a megfelelõ értelemben használjuk. Erre megoldást csak az Ada-95 nyújt, amelynek objektum orientáltsága megteremti a lehetõséget annak kikötésére, hogy egy adott osztály valamely leszármazottjával példányosíthassuk csak a generic-ünket, ezáltal úgy hozzáférve annak mûveleteihez, hogy azoknak jelentésével is tisztában vagyunk.
Az Ada-83 igen fejlett kivételkezelési lehetõségekkel rendelkezik. A kivétel nem feltétlenül jelent hibát, jelenthet csupán kivételes eseményt is. Például:
type Fruits = (APPLE, PEACH, STRAWBERY, BLACKCURRUANT); function Next(F: Fruit) return Fruit is begin return Fruit'SUCC(F); exception when CONSTRAINT_ERROR => return Fruit'FIRST; end Next; |
A fenti programrészletben fellépõ CONSTRAINT_ERROR kivétel nem a hibás mûködés jele, csupán azt mutatja, hogy éppen az utolsó Fruit típusú elemre hívtuk meg a Next függvényt. Ugyanezt a feladatot megoldhattuk volna úgy is, hogy a függvény elején megvizsgáljuk, vajon F egyenlõ-e Fruit'LAST-tal vagy sem. Ez a megoldás azonban kevésbé elegáns, kevésbé olvasható, azonkívül az ember gondolkodásmódjától is távolabb esik, hiszen magunkban is gyakran úgy jegyezzük meg a szabályokat, hogy "általában így..., kivéve ha...".
Az Ada-ban, mint láthattuk vannak elõre definiált
kivételek, ám magunk is definiálhatunk újakat
egyszerûen mint új, exception típusú
azonosítókat. Bármely kivételt magunk is
kiválthatjuk a raise parancs segítségével.
Az Ada kivételkezelése blokk-szintû, azaz az
utasításblokkokhoz rendelhetünk csak
kivételkezelõt úgy, hogy a blokk végén
az exception kulcsszó után a case
utasításhoz hasonló when ágakban felsoroljuk
a kezelni kívánt kivételeket, majd '=>' után
leírjuk a végrehajtandó utasításokat.
Egy when ágban több, egymástól '|'-lal
elválasztott kivételt is elkaphatunk, és
lehetõség van utolsó ágként egy
others ágat is elhelyezni. Ez utóbbival azonban nagyon
óvatosan kell bánni, hiszen történhet bármilyen
hiba, az others ág elkapja. Éppen ezér olyan
formában szokták csak használni, hogy a végén
elhelyeznek egy paraméter nélküli raise
utasítást, amely az éppen lekezelt kivételt
váltja ki újra. Az ilyen others ágaknak
általában az a feladatuk, hogy "mentsék ami menthetõ"
azaz legalább bizonyos dolgokat próbálnak rendbehozni.
Például: típusinvariáns
helyreállítása, file-ok lezárása és
hasonlók.
Az Ada-ban a kezeletlen kivételek tövábbgyûrûznek
a hívó blokkban egészen addig amíg el nem
kapják õket, vagy a legkülsõ blokkból is
kilépve a program abortálását eredményezik.
Párhuzamos folyamatok esetén az egyes taszkok
önálló hibakezeléssel bírnak, ha egy taszk
nem kezel le valamilyen kivételt, akkor komplett állapotba
kerül (ld. Taszkok) de a kivétel nem lép fel semelyik
másik taszkban.
Az Ada kivételkezelésének hiányosságai:
A kivételeinkbõl nem képezhetünk kivétel-csoportokat, kivétel-osztályokat, amelyeket esetleg egységesen lehetne kezelni a kivételkezelõben. (Ellentétben ld. C++, Java)
Kivételeink nem paraméterezhetõek, pedig idõnként hasznos lenne további információt is visszaadni a hiba körülményeirõl. (Ellentétben ld. C++, Java)
A védelem blokkszintû, noha bizonyos esetekben jobb lenne csak
egyetlen kritikus utasítást védeni. Pl. file-nyitás:
ha sikeres akkor használjuk azt a file-t, ha nem akkor a standard
inputot. Ennek kezelése ott helyben nem lehetséges, noha
talán az egész kódrészletben ez az egyetlen kritikus
utasítás.
A probléma azért kezelhetõ, mivel lehetõség
van blokk-utasítás használatára, ám ez
a megoldás meglehetõsen nehézkes, kevéssé
áttekinthetõ.
Nincs lehetõség a kivétel lekezelése után a program folytatására a kivétel helyétõl, pedig bizonyos esetekben (ld. az elõzõ file-nyitásos példa) erre szükség lehet. (Vö. Eiffel)
Nem adhatunk meg úgynevezett végsõ tevékenységet, amelyet minden esetben végre kell hajtani, bár egy külsö blokkba ágyazással azért a feladat megoldható. (Ellentétben ld. C++, Java)
Az Ada-83-ba nem építettek be külön eszközöket a helyességbizonyítás támogatására - ellentétben például az Eiffel-lel - noha õsei között számontartja az Alphard-ot is.
Az Ada-83-at objektum alapú nyelven szokták nevezni. Ez azt
jelenti, hogy lehetõség van objektumok
létrehozására, mégpedig felhasználva a
package-ek láthatósági viszonyait sõt, még
valami osztályszrû dolgot is létrehozhatunk belõluk,
ha az adott package-et generic-ként definiáljuk, ám
az ilyen osztályok között nincs lehetõség
öröklõdésre sõt az ilyen objektumok nem
adhatóak át paraméterként sem. Érdekes
módon ugyanakkor pédául konstruktor könnyen
adható hozzájuk, hiszen generic-et paraméterezhetünk
konkrét értékekkel és a package-ek
törzsében elhelyezhetõek a szükséges
utasítások. Ennek ellenére, mivel az Ada a
private típusokkal igen jól használható
megoldást ad a saját típusok
létrehozására, és mert az ilyen generic-ekbõl
létrehozott objektumok használata nem igazán hatékony
(hiszen a metódusok kódja is duplikálódik minden
új obektummal) és élettartamuk is nagyon statikus, így
nem nagyon használják, legfeljebb egy-egy objetum
létrehozására.
Az Ada-nak létezik objektumorientált változata is, az
Ada-95, amelyben már van nyelvi támogatás az
objektumorientált programozási technikákhoz.
Az Ada-83 párhuzamosságot támogató eszköze a task. Task-ot vagy egyedi objektumként vagy task típusként hozhatunk létre. A task típus limited típus így annak minden megkötése érvényes rá, ám mivel típus így alkalmazható rajta az összes típuskonstrukciós operétor, azaz képezhetõ belõle tömb, beágyazható rekordba, lehet rá access típust definiálni és így futási idõben dinamikusan is hozhatunk létre új taszkokat.
A task-ok szinkronizációjának eszköze az Ada-ban
az entry hívás. Minden taszknak lehetnek entry pontjai,
amelyek hívhatóak más taszkokból. Az entry-t
tartalmazó taskban minden entry-hez tartozik legalább egy
accept utasítás, ahol fogadja az entry
meghívását. Amikor egy ilyen hívás
fogadása bekövetkezik akkor a két taszk
randevújáról beszélünk. Mivel akármelyik
taszk éri is el elõbb a randevú pontját (akár
a hívó az entry hívást, akár a hívott
az accept utasítást), az blokkolódik a radevú
befejeztéig, így ezzel az eszközzel tényleg viszonylag
könnyen megoldhatjuk a szinkronizáció
problémáját.
Mivel az entry-k, az eljárásokhoz hasonlóan,
paraméterezhetõek, így a taskok közötti
információcserének is eszközei lehetnek.
Mint látható, alap kommunikációs modell a blokkoló, szinkron adatcsere.
A taskok közötti adatforgalom másik módja az osztott változók használata. Bármely, több task által látható változó használható osztottan, ám a hozzáférés szinkronizálásáról nekünk kell gondoskodnunk, erre külön nyelvi támogatás nincs (vö. Ada-95 protected típus).
A taskok definíciója két részbõl áll. A specifikációs részbõl, amely tartalmazza a taszk hívható entry-jeit és a törzsbõl, amelyben leírjuk a taszk megvalósítását. Egyetlen megkötés, hogy minden definiált entry-hez kell, ogy tartozzon legalább egy accept utasítás.
Egy task aktivizálása az õt deklaráló
deklarációs rész kiértékelése
után, a törzs elsõ utasításának
végrehajtása elõtt történik. Ha a
deklarációs rész egy package specifikációs
részében van, akkor az aktivizáció elõtt
még kiértékelõdeik a package törzs
deklarációs része is. Ha allokátorral hoztuk
létre a taskot, akkor az allokátor kiértékelése
során következik be az aktivizálása.
Ha a task aktivizálása során hiba lép fel, akkor
a task komplett állapotba kerül és a taskot
tartalmazó programegység törzsében TASKING_ERROR
kivétel váltódik ki.
Az Ada nem különbözteti meg a fõprogramot a tõbbi taszktól legfeljebb annyiban, hogy a fõprogramnak nem lehet entry pontot definiálni. Így azt lehet mondani, hogy a fõprogramot leszámítva minden taskot egy másik task indított el. Azt mondjuk, hogy egy task függ az õ indítójától, mint szülõtõl. Ha egy tasz törzsének futása befejezõdött, vagy valamilyen kezeletlen kivátel lépett fel benne, vagy már a kivétel lekezeláse is véget ért, akkor a task kompletté vált. Egy task terminál ha komplett állapotban van, és minden tõle függõ task terminált már.
A taskkok állapotát két attributumuk segítségével kérdezhetjük le:
Task'CALLABLE - logikai érték, ami akkor igaz, ha a task még hívható, azaz nem komplett, nem terminált és nem abortált.
Task'TERMINATED - akkor igaz, ha az adott task már terminált.
Entry-k definiálásakor megadhatunk egyszerû entryket vagy úgynevezett entry-családot, ami egy diszkrét intervallum elemeivel indexelt. Amikor egy új task objektumot hozunk létre, minden entry-jéhez létrejön egy várakozási sor. Ebbe kerülnek az adott entry-t hívók mindaddig, amíg egy megfelelõ accept utasításhoz nem ér a hívott task a végrehajtása során. Minden entry-hez tartozik egy 'COUNT nevû attributum, amellyel lekérdezhetõ, hogy hányan vannak éppen a várakozási sorában.
További eszközöket nyújt, bizonyos helyzetek megoldására a select utasítás. A legjellemzõbb a következõ három:
Szelektív várakoztatás:
Lehetõvé teszi, hogy többféle accpet utasítás is "érvényben legyen", azaz a task egyszerre többféle entry-jének hívását is hajlandó fogadni. Kell lennie legalább egy olyan ágnak, amely accept utasítást tartalmaz, ezenkívül lehet delay és terminate ágat is megadni. Ezenkívül megadható egy else ág is. Az egyes ágakhoz úgynevezett õrfeltételeket rendelhetünk. Egy ágat nyitottnak nevezünk, ha nincs õrfeltétele vagy az igaz. Az utasítás végrehajtásakor kiértékelésre kerülnek az õrfeltételek. Ha van nyitott ág akkor egy olyan, ha nincs akkor az else ág hajtódik végre. További szabályok:
ha van nyitott accept ág, amelynél randevú jün létre, akkor az hajtódik végre
ha nincs nyitott accept ág, akkor egy nyitott delay ág váalsztódik ki
else ág csak az összes ág zártsága esetén hajtható végre
terminate ág csak akkor választódhat ki, ha a taszk minden entrysora üres.
A feltételes entry hívás csak akkor hozza létre a hívást, ha az azonnal létrejön. Egyéb esetekben az else ág hajtódik végre.
Ebben az esetben a randevú akkor jön csak létre, ha a delay ágban megadott idõtartam alatt a hívás fogadása bekövetkezik. Egyébként a select utasítás végrehajtása befejezõdik.