A Fortran programozási nyelv

A Fortran 2003 újdonságai



Bevezető

E dokumentum célja a Fortran 2003 szabvány által bevezetett újdonságok bemutatása, főként a Fortran 95-ös szabványához és a kvázi-szabványnak minősülő két hozzá tartozó technikai riporthoz képest.

Ez a szöveg egy angol nyelvű cikk elejének a fordítása. Az eredeti dokumentum: John Reid - The New Features of Fortran 2003.

Újdonságok

Ezeken túl még több kisebb módosítás is történt, ezekre a fordítás folytatója kitérhet, ha jónak látja.

Bővebb adattípus létrehozási lehetőségek és objektumorientáltság

Paraméterezett típusszármaztatás

Kétségtelen hiányossága volt a F95-nek, hogy míg az összes beépített típus rendelkezett egy ún. kind paraméterrel, valamint a karakter típus egy hossz paraméterrel, addig egyetlen összetett származtatott típust sem lehetett paraméterezni. Ezt a hiányosságot pótolja a egy új, igen rugalmas eszköz, amely bármely típushoz akármennyi 'kind' és 'length' jellegű paramétert engedélyez.

A kind paraméter egy fordítási időben ismert konstans, amely egy tetszőleges beépített vagy származtatott típus kind-paramétere lehet. A hossz (length) paraméter a F95-ből ismert karakter típus hosszának mintájára, bármely karakterlánc típus vagy tömb hosszának meghatározására szolgálhat.

A típusparaméterek neveit a TYPE utasítással adhatjuk meg a típusdefinícióban, úgy mint az eljárások és függvények ún. 'dummy'-argumentumait és vagy KIND-nak vagy LEN-nek kell deklarálni őket. Alapértelmezett érték megadására is lehetőség van. A következő példa egy egyszerű mátrix-típust definiál:

TYPE matrix(kind,m,n)
INTEGER, KIND :: kind=KIND(0.0D0)! gives a default value
INTEGER, LEN :: m,n ! no default value given
REAL(kind) :: element(m,n)
END TYPE

A típusparaméterek konkrét értékei általában egy a definícióval azonos típusú objektummal vannak specifikálva:

TYPE(matrix(KIND(0.0D0),10,20)) :: a

A fenti utasítás egy 10x20-as dupla pontos típusból álló mátrixot deklarál. Egy pointer vagy allocatable típushoz vessző használható a hossz megadásához:

TYPE(matrix(KIND(0.0),:,:)),ALLOCATABLE :: a

Az aktuális paraméter akkor kerül kiértékelésre, amikor az objektum lefoglalásra kerül, vagy a mutatót ráállítjuk. A dummy-argumentumnál csillag jelöli a feltételezett értéket. A kind-paraméternél az értéknek egy inicializáló utasításnak kell lennie.

Az eljáráshívás szintaxisának kulcsszavai használhatók:

TYPE(matrix(kind=KIND(0.0),m=10,n=20)) :: a

Ugyanez a szintaxis használható más származtatott típusú adattagok deklarálásakor:

TYPE double_matrix(kind,m,n)
INTEGER, KIND :: kind
INTEGER, LEN :: m,n
TYPE(matrix(kind,m,n)) :: a,b
END TYPE

Amennyiben a paraméternek van alapértelmezett értéke, kihagyható a paraméterértékek listájából.

A típusparaméterek komponenseinek lekérdezése a következőképpen zajlik:

a%kind, a%m

Természetesen ezzel a szintaxissal nem lehet a típusparaméterek értékeit megváltoztatni, ha például az értékadás baloldalán szerepelne a típusparaméter. Kizárólag a lekérdezésre nyílik mód. Ezzel a szintaxissal egy beépített típusú objektum típusparaméterét is le lehet kérdezni:

LOGICAL :: L
WRITE (*,*) L%KIND ! Same value as KIND(L)

Eljárásmutatók

Egy mutató lehet alprogrammutató, lehet explicit vagy implicit interfésze és társítása a mutatott alprogrammal a dummy-eljárásokéhoz hasonló, azaz az interfész nem lehet sem generikus sem elemi.

A következő utasítás:

PROCEDURE (proc), POINTER :: p => NULL()

p-t egy eljárásmutatónak deklarálja, eredetileg 'null'-nak és az interfésze a 'proc' eljáráséval azonos.

Amennyiben egy megfelelő eljárás sem áll rendelkezésre, hogy sablonparaméterként szolgálhasson, akkor egy ún. absztrakt interfész is használható:

ABSTRACT INTERFACE
REAL FUNCTION f(a,b,c)
REAL, INTENT(IN) :: a,b,c
END FUNCTION
END INTERFACE

anélkül, hogy lenne egy ilyen konkrét f eljárás.

Ugyanúgy, mint az adatstruktúrákra mutató mutatók esetében az eljárásmutatók lehetnek inicializálatlanok, vagy null-ként inicializáltak. A következő utasítás:

PROCEDURE ( ), POINTER :: p

p-t inicializálatlanul hagyja egy implicit interfésszel. Később rá lehet állítani p-t egy eljárásra vagy függvényre. A következő utasítás:

PROCEDURE (TYPE(matrix(KIND(0.0D0),m=10,n=20))), POINTER :: p

hasonló az előzőhöz, csak azzal a különbséggel, hogy a mutatott függvény visszatérési értéke egy adott típusnak és típusparamétereknek kell hogy megfeleljen.

A függvényeknek lehet eljárásmutató a visszatérési értékük is.

A mutatóknak való értékadás a megszokott módon történik:

p => proc

Az interfészeknek meg kell egyezniük úgy mint az aktuális alprogram paramétereknek. A jobboldal lehet egy eljárás, eljárásmutató vagy egy függvény, aminek a visszatérési értéke egy eljárásmutató.

Az eljárásmutatók léte egy igen nagy hiányosságot pótol a F95-höz képest: segítségével lehetőség nyílik arra, hogy az eljárásokat és függvényeket az objektumokkal együtt kezeljük (dinamikus kötés):

TYPE matrix(kind,m,n)
INTEGER, KIND :: kind
INTEGER, LEN :: m,n
REAL(kind) :: element(m,n)
PROCEDURE (lu), POINTER :: solve
END TYPE
:
TYPE(matrix(KIND(0.0D0),m=10,n=20))) :: a
:
CALL a%solve(....
:

Ha az eljárás mindig ugyanaz, akkor ahelyett, hogy az objektummal együtt kezelnénk, jobban tesszük, ha társítjuk a típushoz.

Finalization

Némely származtatott típushoz ún. final alprogramok kötődhetnek (bind). Ezek célja általában valamiféle erőforrásfelszabadítás jellegű dolog, például mutatott memóriaterületek felszabadítása amikor az adott objektum megsemmisül. Minden final alprogram egy modul-eljárás, melynek egyetlen paramétere az éppen megsemmisülésre kerülő objektum az adott származtatott adattípusból.

A final modul-eljárások definiálásának szintaxisa a következő:

TYPE T
: ! Component declarations
CONTAINS
FINAL :: finish1, finish2
END TYPE T

Egy származtatott típus akkor finalizálható, ha van neki final alprogramja, vagy ha van olyan komponense, ami finalizálható és se nem pointer se nem allocable típusú. Egy nem pointer adatobjektum akkor finalizálható, ha a típusa finalizálható. Amikor egy ilyen objektum élettartama lejár és megsemmisül, lefut a finalizációs alprogramja, amennyiben létezik megfelelő paraméterű. Ezután minden finalizálható komponens finalizátora lefut. (Amennyiben ezek bármelyike tömb, akkor a tömb minden elemének finalizátora is lefut.) Tehát egy beágyazott típusnál ez a top-down elv azzal jár, hogy a finalizátornak csak a nem finalizálható komponensekkel kell foglalkoznia.

Eljárások név általi hozzárendelése típusokhoz

Egy eljárás hozzá lehet rendelve egy skalár objektumhoz és hozzáférhető onnan a komponenskiválasztás szintaxisával ahelyett, hogy az objektum egy eljárásmutatója mutatna rá.

Ezt a szintaxist mutatja be a következő példa:

TYPE T
: ! Component declarations
CONTAINS
PROCEDURE :: proc => my_proc
PROCEDURE :: proc2
END TYPE T

Ez hozzárendeli (bind-olja) a proc nevet a my_proc-hoz és a proc2 nevet önmagához. Mindkét eljárásnak egy modul-eljárásnak kell lennie vagy egy külső eljárásnak egy expicit interfésszel. Ha 'a' egy skalár változó a 'T' típusból, akkor a típushozzárendeléses hívásra egy példa a következő:

CALL a%proc(x,y)

Sok hasonló eljárás hozzáférhető egy általános névvel. A 'PROCEDURE' utasítás helyét átveheti a 'GENERIC' kulcsszó:

GENERIC :: gen => proc1, proc2, proc3

ahol a proc1, proc2 és proc3 a konkrét hozzárendelt nevek. A függvényhívások egyértelműségére szokásos szabályok alkalmazandóak természetesen a generikus név által elért eljárásokra is.

A PASS attribútum

Egy eljárásnak, amely egy komponens vagy egy hozzárendelt név által hozzáférhető, gyakran lehet szüksége arra, hogy hozzáférjen a skalár objektumhoz, amelyen keresztül meghívták. Az alapértelmezett módon ez az objektum az első paraméter, mint például itt:

CALL a%proc(x,y)

A fenti példában az 'a' objektum lenne az eljárás első paramétere, 'x' a második és 'y' a harmadik. Ehhez az szükséges, hogy az első paraméter egy megfelelő típusú skalár objektum legyen és ekkor az eljárás rendelkezik a megfelelő PASS attribútummal. Ha ez a viselkedési mód nemkívánatos, akkor explicit módon definiálható a NOPASS attribútum:

PROCEDURE, NOPASS, POINTER :: p

A gyakrabban használt PASS attribútum explicit módon ki is írható:

PROCEDURE, PASS(arg) :: proc3

Az ilyen módon átadott argumentum nem lehet sem pointer, sem allocatable és az összes length-típusú típusparamétere ismert kell hogy legyen.

Eljárások, mint operátorok hozzárendelése típusokhoz

Egy eljárás hozzá lehet rendelve egy adott típushoz, mint operátor, vagy adott típusú értékhozzárendelés. Ebben az esetben az eljárás mindenütt hozzáférhető, ahol az adott típusba tartozó objektum elérhető. A szintaxis a 'GENERIC' utasítások szintaxisával működik az operátort tartalmazó típus definíciójában:


TYPE matrix(kind,m,n)
INTEGER, KIND :: kind
INTEGER, LEN :: m,n
REAL(kind) :: element(m,n)
CONTAINS
GENERIC :: OPERATOR(+) => plus1, plus2, plus3
GENERIC :: ASSIGNMENT(=) => assign1, assign2
! plus1 and assign1 are for matrices alone.
! The others are for mixtures with other types.
END TYPE
:
TYPE(matrix(KIND(0.0D0),m=10,n=20))) :: a,b,c
:
a = b + c ! Invokes plus1, then assign1.
:

ahol legalább az egyik, vagy mindkét argumentumnak a kapcsolódó típusból kell származnia. A függvényhívások egyértelműsítésének szabályai az egy objektum által elérhető operátorok körében is érvényesnek kell maradniuk.

Típuskiterjesztés

Azon származtatott adattípusok kiterjeszthetők, amelyeknek nincsen sem 'SEQUENCE' sem 'BIND' attribútuma. A fenti mátrix típus például kiterjeszthető:

TYPE, EXTENDS(matrix) :: factored_matrix
LOGICAL :: factored=.FALSE.
REAL(matrix%kind) :: factors(matrix%m,matrix%n)
END TYPE

Ezzel szemben egy típus amelynek van SEQUENCE vagy BIND attribútuma nem kiterjeszthető.

Egy kiterjesztett típus szintén kiterjeszthető, így az "őstípus" kifejezés arra a típusra használandó, amelyből közvetlenül kiterjesztéssel származik az adott típus. Az összes típusparaméter, komponens és hozzárendelés öröklődik az őstípusból és mindegyik az eredeti nevén. Például:

TYPE(factored_matrix(kind(0.0),10,10)) :: f

egy 10x10-es faktorált valós számokból álló mátrixot deklarál. A típusparaméterei az f%kind, f%m és f%n kifejezésekkel kérdezhetőek le. Az örökölt komponens pedig az f%element kifejezéssel hivatkozható.

Ezen túl a kiterjesztett típus rendelkezik még azokkal a komponensekkel, típusparaméterekkel és hozzárendelt függvényekkel, amelyeket a saját definíciója tartalmaz. Itt például f%factored és f%factors.

A kiterjesztett típus rendelkezik egy speciális komponenssel, az őstípus komponensével, amelynek típusparaméterei, komponensei és őstípusa az őstípuséval megegyeznek. Történetesen ezt ki is használta a fenti factored_matrix példa. Az örökölt paraméterek, komponensek és társított eljárások hozzáférhetőek egyben az f%matrix szintaxissal és egyenként is, például f%n vagy f%element. Az ősön keresztül is egyenként hozzáférhetőek: f%matrix%n és f%matrix%element.

A nem szülő komponensek között fennáll egy sorrendiség, ami a rekordkonstruktoroknál vagy az I/O-nál fontossá válik. Az örökölt komponensek az ősbeli sorrendjük szerint, amiket az új komponensek követnek. Itt például: element, factored, factors.

Egy rekordkonstruktorban értéket adhatunk az ős komponenseknek és az örökölt komponenseknek is, de figyelnünk kell arra, hogy nem specifikálható egyetlen komponens sem egynél többször és azok komponensek, amelyek nem rendelkeznek default értékkel, azoknak pontosan egyszer kell értéket adni.

Típushoz rendelt eljárások felüldefiniálása

Egy típushoz név által hozzárendelt eljárás rendelkezhet a szülőhöz rendelt eljárás nevével és attribútumaival, a PASS-argumentum értékétől függetlenül. Nem feltétlenül kell PRIVATE-nek lennie, ha a szülő társítása PUBLIC. Ebben az esetben felüldefiniálja a szülő társítását. Ez megakadályozható a következőképpen:

PROCEDURE, NON_OVERRIDABLE :: proc2

Felsorolási típus

Egy felsorolási típus valójában nem más, mint integer konstansok összessége, ami alkalmas arra, hogy a C nyelvvel együttműködést biztosítson. A felsorolt értékek típusa ahhoz az int típushoz hasonló, amit a C nyelv is használ, például:

ENUM, BIND(C)
ENUMERATOR :: RED = 4, BLUE = 9
ENUMERATOR YELLOW
END ENUM

ami a 4, 9 és 10 felsorolási értékeket definiálja.

Amennyiben egy értéknek nem adunk explicit módon értéket, akkor az az előzőnél egyel nagyobb lesz.

Az ASSOCIATE konstrukció

Az 'ASSOCIATE' konstrukció segítségével névvel rendelkező identitásokhoz rendelhetünk kifejezéseket vagy változókat a végrehajtási blokkban. Néhány példa:

ASSOCIATE ( Z => EXP(-(X**2+Y**2)) * COS(THETA) )
PRINT *, A+Z, A-Z
END ASSOCIATE

ASSOCIATE ( XC => AX%B(I,J)%C, ARRAY => AX%B(I,:)%C )
XC%DV = XC%DV + PRODUCT(XC%EV(1:N))
ARRAY = ARRAY + 1.0
END ASSOCIATE

A neveket hozzárendelt (asszociált) névnek (associate name) nevezzük.

Az ASSOCIATE utasításban szereplő kifejezések az utasítás végrehajtásakor kerülnek kiértékelésre és ez után ez az érték használható. Egy asszociált objektum nem használható olyan esetben, hogy az értéke megváltozzon, hacsak nem egy változóhoz van hozzárendelve.

A konstrukció egyéb konstrukciókba a szokásos módon ágyazható be.

Polimorfikus entitások

Egy polimorfikus entitás tetszőleges típusúnak deklarálható, a CLASS kulcsszó segítségével, ha a TYPE helyett használjuk és ekkor felveheti ezt a típust vagy ennek bármely kiterjesztését a végrehajtás során. A típus a végrehajtás egy adott pontján dinamikus típusnak nevezzük. Az entitásnak pointer vagy allocatable típusúnak kell lennie (esetleg dummy argument-nek) és dinamikus típusát a lefoglaláskor, pointer-értékadáskor vagy paramétermegfeleltetéskor kapja.

A mechanizmus lehetőséget biztosít arra, hogy azt a kódot, amit egy adott típusra írtak, később felhasználható legyen e típus kiterjesztettjeinek a kezelésére. Egy entitás 'típuskompatibilis' az azonos típusú objektumokkal és azokkal amelyek típusa ezen típus kiterjesztettje

Egy objektum akár a 'CLASS (*)' típussal is deklarálható, ez az úgynevezett korlátlanul polimorfikus típus. Ekkor az objektum nem tartozik egy típusba egyetlen másik objektummal sem, de mindegyikkel típuskompatibilis lesz.

Egy dummy argument, amely sem allocatable sem pointer, az típuskompatibilis kell hogy legyen az aktuális paraméterrel. Ez például lehetővé teszi egy F95 eljárás számára hogy paramétert adjon át egy kiterjesztett típusból. Egy allocatable vagy pointer típusú formális paraméter azonos típussal kell hogy rendelkezzen, mint az aktuális paraméter és amennyiben az egyik polimorfikus, a másiknak is annak kell lennie. Egy formális paraméter a PASS-attribútummal polimorfikus kell hogy legyen, amennyiben a típusa kiterjeszthető.

A származtatott típusok értékadásának lehetősége kibővült a lehetőséggel, hogy a jobb oldal (és csak az) polimorfikus is lehet. A deklarált típusoknak a szokásos módon meg kell felelniük egymásnak, de a jobboldal dinamikus típusa lehet a baloldal típusának kiterjesztése is. Ebben az esetben a jobboldal komponensei páronként megfeleltetésre kerülnek a baloldal komponenseinek és az értékek egyenként átmásolódnak.

Pointerértékadásnál a fő szabály az, hogy a mutatónak típuskompatibilisnek kell lennie a mutatott típussal és a kind típusparaméterek értékeinek azonosnak kell lenniük a két típusnál. Amennyiben ez polimorfikus, akkor a dinamikus típuséval kell egyeznie. Ez alól a szabály alól van egy kivétel: ha a mutató többszörösen származtatott típusú és a mutatott típus korlátlanul polimorfikus, de a dinamikus típusa az adott származtatott típus.

A típusparaméterek, komponensek és hozzárendelt függvények csak a deklarált típus által közvetlenül hozzáférhetőek, bár további hozzáférés elérhető a SELECT TYPE konstrukció segítségével.

A következő lekérdező függvények

SAME_TYPE_AS(A,B)
EXTENDS_TYPE_OF(A,MOLD)

segítségével kérdezhető le, hogy két objektum azonos dinamikus típussal rendelkezik-e, vagy hogy egy adott objektum dinamikus típusa egy adott típus kiterjesztettje-e.

A SELECT TYPE konstrukció

A SELECT TYPE szerkezet feltételes ágai közül legfeljebb az egyik kerül végrehajtásra egy adott kifejezés vagy objektum dinamikus típusától függően. A következő egyszerű példa mutatja ezt:

CLASS (matrix(kind(0.0),10,10)) :: f
:
SELECT TYPE (ff => f)
TYPE IS (matrix)
: ! Block of statements
TYPE IS (factored_matrix)
: ! Block of statements
END SELECT

Az első blokk akkor kerül végrehajtásra, ha 'f' dinamikus típusa 'matrix', a második pedig akkor, ha 'factored_matrix'. Az f 'szelektor' társítása a társított nevével (ff) ugyanúgy történik, mint az ASSOCIATE szerkezetnél. A második blokkban az ff-et használhatjuk arra, hogy elérjük a kiterjesztéseket, mint például: ff%factored.

Egy bővebb példa a SELECT TYPE szerkezetre:

TYPE :: POINT
REAL :: X, Y
END TYPE POINT
TYPE, EXTENDS(POINT) :: POINT_3D
REAL :: Z
END TYPE POINT_3D
TYPE, EXTENDS(POINT) :: COLOR_POINT
INTEGER :: COLOR
END TYPE COLOR_POINT
TYPE(POINT), TARGET :: P
TYPE(POINT_3D), TARGET :: P3
TYPE(COLOR_POINT), TARGET :: C
CLASS(POINT), POINTER :: P_OR_C
P_OR_C => C
SELECT TYPE ( A => P_OR_C )
TYPE IS ( POINT_3D )
PRINT *, A%X, A%Y, A%Z
CLASS IS ( POINT )
PRINT *, A%X, A%Y ! This block gets executed
END SELECT

Minden blokkon belül a hozzárendelt névnek a TYPE IS típusa vagy CLASS IS osztálya van. A megfelelő blokk kiválasztása a következő elven történik:

Késleltetett kötések és absztrakt típusok

Vannak helyzetek, amikor azt várjuk, hogy egy adott függvényt, amely egy típushoz van kötve csak az adott típus azon kiterjesztései fogják meghívni, amelyek felüldefiniálják az adott függvényt. Ez a leggyakrabban akkor fordul elő, amikor a típushoz kötött függvénynek nincs alapértelmezett vagy természetes implementációja, csak jól definiálható célja és felülete. Az ilyen viselkedés biztosítható azzal ha a típust absztraktnak és a kötést késleltetettnek definiáljuk. A kötött függvény esetén szükséges az absztrakt interfész. Egy egyszerű példa a következő:

TYPE, ABSTRACT :: FILE_HANDLE
CONTAINS
PROCEDURE (OPEN_FILE), DEFERRED, PASS :: OPEN
...
END TYPE
ABSTRACT INTERFACE
SUBROUTINE OPEN_FILE(HANDLE)
CLASS(FILE_HANDLE),INTENT(INOUT) :: HANDLE
END SUBROUTINE OPEN_FILE
END INTERFACE

Ebben a példában azt feltételezzük, hogy a típus kiterjesztései adatokat fognak tartalmazni a fájlról és az OPEN függvényt felüldefiniálják úgy, hogy ezeket az adatokat használja a megnyitáshoz.

A polimorfikus entitások absztrakt típussal is deklarálhatók, de nem lehetséges deklarálni, lefoglalni vagy konstruálni egy nem polimorfikus absztrakt típusú objektumot. A késleltetett kötések is csak absztrakt típus esetén megengedettek.

Egyéb fejlesztések

Rekordkonstruktorok

Egy rekordkonstruktor értéklistája egy aktuális paraméterlista szintaxisával írható le a komponenseknek megfelelő kulcsszavakkal. Azon komponensek, amelyekhez a definícióban alapértelmezett érték lett megadva, kihagyhatóak a listáról. Ezáltal rekordkonstruktorok használhatók privát komponensekkel rendelkező rekordokhoz is, amennyiben ezek a privát komponensek rendelkeznek alapértelmezett értékkel. Természetesen egy komponensnek nem adhatunk értéket egynél többször és az aktuális érték felülírja az alapértelmezett értéket. Amennyiben a típus rendelkezik típusparaméterekkel, akkor az alapértelmezett értékkel nem rendelkező paramétereket meg kell adni:

a = matrix(KIND(0.0),m=10,n=20) (element = 0.0)

Egy generikus név megegyezhet egy származtatott típus nevével, ebben az esetben ez a típus konstruktorának felüldefiniálásával vagy túlterhelésével jár.

Az allocate utasítás

Az allocatable attribútum nem kizárólag a tömbök sajátja és egy forrásváltozó deklarálható úgy, hogy értékül szolgáljon a késleltetett típusparamétereknek és kezdeti értékül magának a typusnak.

Például:

TYPE(matrix(KIND(0.0D0),m=10,n=20)) :: a
TYPE(matrix(KIND(0.0D0),m=:,n=:)),ALLOCATABLE :: b, c
:
ALLOCATE(b,SOURCE=a)
ALLOCATE(c,SOURCE=a)

a fenti példában lefoglalásra kerülnek a 'b' és 'c' skalár objektumok, melyek 10x20-as mátrixok lesznek az 'a' értékkel. A SOURCE kifejezés típusának a lefoglalásra kerülő objektum típusával kell egyeznie, vagy annak kiterjesztése kell hogy legyen és skalárnak vagy egy a lefoglalásra kerülő objektummal azonos méretű tömbnek kell lennie.

Ezen kívül a késleltetett típusparamétereket a lefoglaló utasításban is meg lehet adni:

ALLOCATE ( matrix(KIND(0.0D0),m=10,n=20) :: b,c )

Amennyiben ezt a módot használjuk a lefoglaló utasításban, akkor a forrásból való inicializálás nem használható. Vagy az egyik vagy a másik használandó, amennyiben késleltetett típusparamétereket használunk. Amennyiben az egyiket használjuk, minden lefoglalandó objektum a listában ugyanazzal a nem késleltetett típusparaméterrel kell, hogy rendelkezzen, mint a forrásváltozó vagy a típusdeklaráció.

Az allocate utasítás megadhatja egy polimorfikus objektum dinamikus típusát is:

CLASS (matrix(kind(0.0),10,10)) :: a,b,c,d
:
ALLOCATE(factored_matrix(kind(0.0),10,10) :: b,c)
ALLOCATE(d,SOURCE=a) ! d takes its dynamic type from a

Egy ALLOCATE vagy DEALLOCATE utasítás opcionálisan tartalmazhat egy ERRMSG= módosítót, amely egy tetszőleges karakter skalár változót azonosíthat. Amennyiben hiba lép fel a lefoglaló utasítás közben, a végrehajtó egy magyarázó hibaüzenetet rendel az adott változóhoz. Ha ilyen helyzet nem áll elő, a változó értéke nem változik.

A KIND paraméter

A beépített adattípusok (INTEGER, REAL, COMPLEX, LOGICAL, CHARACTER) mind rendelkeznek hozzárendelt ún. kind paraméterrel. Az, hogy mely beépített típusok ténylegesen milyen paraméterértékekkel rendelkezhetnek az implementációfüggő. A Fortran szabványa mindössze annyit ír elő, hogy INTEGER-nek kell lenniük és mind a REAL mind a COMPLEX típusoknak legalább két kind paraméterrel kell rendelkezniük és legalább egy kell hogy legyen az INTEGER ,CHARACTER és LOGICAL típusoknak.

Együttműködés a C nyelvel

Bevezetés

A Fortran 2003 egységes szabványt biztosít a C nyelvel való együttműködésre. Nyilvánvalóan, bármely entitásnak ugyanolyan deklarációjának kell lennie a két nyelvben. Ez Fortran programban érvényesül ahol megköveteljük, hogy minden hasonló entitás „együttműködő” legyen. A következőkben megvizsgáljuk, hogy pontosan mit várunk el a típusok, változók és eljárások szempontjából. Mindegyik elvárás a szintaxisra vonatkozik, tehát a fordító már fordítási időben tudja, hogy egy entitás együttműködő-e.

Együttműködés belső típusokkal

Létezik a Fortranban egy ISO_C_BINDING modul, melynek nevesített konstansai kind típus paraméter értékeket tartalmaznak a belső típusokhoz. A processzornak nem szükséges az összes nevesített konstanst támogatnia. A támogatás hiányát negatív érték jelöli.

Típus Nevesített konstans C típu vagy típusok
INTEGER C_INT int
INTEGER C_SHORT short int
INTEGER C_LONG long int
INTEGER C_LONG_LONG long long int
INTEGER C_SIGNED_CHAR signed char, unsigned char
INTEGER C_SIZE_T size_t
INTEGER C_INT8_T int8_t
INTEGER C_INT16_T int16_t
INTEGER C_INT32_T int32_t
INTEGER C_INT64_T int64_t
INTEGER C_INT_LEAST8_T int_least8_t
INTEGER C_INT_LEAST16_T int_least16_t
INTEGER C_INT_LEAST32_T int_least32_t
INTEGER C_INT_LEAST64_T int_least64_t
INTEGER C_INT_FAST8_T int_fast8_t
INTEGER C_INT_FAST16_T int_fast16_t
INTEGER C_INT_FAST32_T int_fast32_t
INTEGER C_INT_FAST64_T int_fast64_t
INTEGER C_INTMAX_T intmax_t
INTEGER C_INTPTR_T intptr_t
REAL C_FLOAT float
REAL C_DOUBLE double
REAL C_LONG_DOUBLE long double
COMPLEX C_FLOAT_COMPLEX float _Complex
COMPLEX C_DOUBLE_COMPLEX double _Complex
COMPLEX C_LONG_DOUBLE_COMPLEX long double _Complex
LOGICAL C_BOOL _Bool
CHARACTER C_CHAR char

A karakter típus együttműködése megköveteli azt is, hogy a hossz paraméter elhagyható legyen, vagy egy kezdeti kifejezés adja meg azt, melynek értéke 1. Az alábbi nevesített konstansok támogatottak (a jelentésük értelemszerűen): C_NULL_CHAR, C_ALERT, C_BACKSPACE, C_FORM_FEED, C_NEW_LINE, C_CARRIAGE_RETURN, C_HORIZONTAL_TAB, C_VERTICAL_TAB.

Együttműködés C pointerekkel

A C pointerekkel való együttműködés érdekében (melyek csak címek), a modul tartalmazza a C_PTR és C_FUNPTR származtatott típusokat, melyek együttműködnek a C pointerekkel (értelem szerűen). Megtalálhatóak a C_NULL_PTR and C_NULL_FUNPTR nevesített konstansok, melyek megfelelnek a C null értékének.

A modul továbbá tartalmazza az alábbi eljárásokat:

Együttműködés származtatott típusokkal

Ahhoz, hogy egy származtatott típus együttműködő legyen, explicit meg kell adni a BIND attribútumot:

TYPE, BIND( C ) ::MYTYPE ... END TYPE MYTYPE

Minden egyes elem típusának és típus paraméterének együttműködőnek kell lenni, nem lehet pointer és nem lehet allokálható.

Például

typedef struct { int m, n; float r; } myctype

együttműködik az alábbi FORTRAN típussal:

USE ISO_C_BINDING TYPE, BIND(C) :: MYFTYPE INTEGER(C_INT) :: I, J REAL(C_FLOAT) :: S END TYPE MYFTYPE

A típus neve és az elemek nevei nem számítanak az együttműködés szempontjából. A Fortran típusok nem tudnak együttműködni a C unió típussal és olyan struktúrákkal, melyek változó méretű tömböt vagy bitmezőt tartalmaznak.

Változók együttműködése

Egy változó Fortranban együttműködő, ha együttműködő típusú és típus paraméterű, ezen kívül nem pointer és nem is allokálható.

Fortranban egy tömb együttműködő, ha együttműködő típusú és típus paraméterű és explicit megadjuk a formáját vagy a feltételezett méretét. Együttműködik hasonló típusú, típusparaméterű és alakú C tömbökkel, de fordított indexekkel. Például az alábbi két tömb együttműködő:

INTEGER :: A(18, 3:7, *) int b[][5][18]

Eljárások együttműködése

Egy Fortran függvény együttműködő ha rendelkezik explicit interfésszel és a BIND attribútummal deklaráltuk:

FUNCTION FUNC(I, J, K, L, M), BIND(C)

Minden argumentumnak együttműködőnek kell lennie. Függvényeknél a visszatérési értéknek skalárnak és együttműködőnek kell lennie. Az eljárásoknak van egy "binding" címkéje ami globális, és ezt a címkét fogja látni a C program. Alapértelmezetten ez az eljárás Fortranbeli neve kisbetűvel írva. Például, az alábbi függvény binding címkéje func. Alternatív címkét is megadhatunk:

FUNCTION FUNC(I, J, K, L, M), BIND(C, NAME='C_Func')

Az ilyen eljárások felelnek meg a megfelelő C függvényeknek a megfelelő binding címkével. Függvények esetén a visszatérési érték típusának együttműködőnek kell lennie a prototípus visszatérési tíusával. Eljárások esetén a a prototípusnak void visszatérési típusának kell lennie.

Globális változók együttműködése

Az együttműködő modul változóknak vagy a common block együttműködő tagjainak meg kell adni a BIND attribútumot:

USE ISO_C_BINDING INTEGER(C_INT), BIND(C) :: C_EXTERN INTEGER(C_LONG) :: C2 BIND(C, NAME='myVariable') :: C2 COMMON /COM/ R, S REAL(C_FLOAT) :: R, S BIND(C) :: /COM/

A binding címkére ugyanazok vonatkoznak, mint eljárások esetén.

Példa C hívásra Fortranból

C függvény prototípus

int C_Library_Function(void* sendbuf, int sendcount, int *recvcounts)

Fortran modul

MODULE FTN_C INTERFACE INTEGER (C_INT) FUNCTION C_LIBRARY_FUNCTION & (SENDBUF, SENDCOUNT, RECVCOUNTS), & BIND(C, NAME='C_Library_Function') USE ISO_C_BINDING IMPLICIT NONE TYPE (C_PTR), VALUE :: SENDBUF INTEGER (C_INT), VALUE :: SENDCOUNT TYPE (C_PTR), VALUE :: RECVCOUNTS END FUNCTION C_LIBRARY_FUNCTION END INTERFACE END MODULE FTN_C

Fortran hívás

USE ISO_C_BINDING, ONLY: C_INT, C_FLOAT, C_LOC USE FTN_C ... REAL (C_FLOAT), TARGET :: SEND(100) INTEGER (C_INT) :: SENDCOUNT INTEGER (C_INT), ALLOCATABLE, TARGET :: RECVCOUNTS(:) ... ALLOCATE( RECVCOUNTS(100) ) ... CALL C_LIBRARY_FUNCTION(C_LOC(SEND), SENDCOUNT, & C_LOC(RECVCOUNTS)) ...

Példa Fortran hívásra C-ből

Fortran kód

SUBROUTINE SIMULATION(ALPHA, BETA, GAMMA, DELTA, ARRAYS), BIND(C) USE ISO_C_BINDING IMPLICIT NONE INTEGER (C_LONG), VALUE :: ALPHA REAL (C_DOUBLE), INTENT(INOUT) :: BETA INTEGER (C_LONG), INTENT(OUT) :: GAMMA REAL (C_DOUBLE),DIMENSION(*),INTENT(IN) :: DELTA TYPE, BIND(C) :: PASS INTEGER (C_INT) :: LENC, LENF TYPE (C_PTR) :: C, F END TYPE PASS TYPE (PASS), INTENT(INOUT) :: ARRAYS REAL (C_FLOAT), ALLOCATABLE, TARGET, SAVE :: ETA(:) REAL (C_FLOAT), POINTER :: C_ARRAY(:) ... ! Associate C_ARRAY with an array allocated in C CALL C_F_POINTER (ARRAYS%C, C_ARRAY, (/ARRAYS%LENC/) ) ... ! Allocate an array and make it available in C ARRAYS%LENF = 100 ALLOCATE (ETA(ARRAYS%LENF)) ARRAYS%F = C_LOC(ETA) ... END SUBROUTINE SIMULATION

C struct deklaráció

struct pass {int lenc, lenf; *c, float* f}

C függvényprototípus

void simulation(long alpha, double *beta, long *gamma, double delta[], struct pass *arrays)

C hívás

simulation(alpha, &beta, &gamma, delta, &arrays);