Arbeiten mit Fortran in 2020: Do’s and Don’ts
Ein Knigge für Fortran-Developer
von DI Dr. Christoph Hofer
Der Name Fortran setzt sich zusammen aus “FORmel TRANslator”und ist eine der ältesten Programmiersprachen. Für viele Softwareentwickler ist sie der Archetyp für eine alte, behäbige, eingeschränkte und schwer zu verstehende Programmiersprache mit der man am besten nichts zu tun haben möchte. Für die alten Versionen von Fortran mag dieses Vorurteil wirklich wahr sein. Fortran hat sich in seiner langen Geschichte aber viel verändert, sodass in seiner “modernen” Variante (wie etwa Fortran 2003) die Sprache einen viel schlechteren Ruf hat als ihr zusteht. Der typische Use-Case für Fortran als Programmiersprache sind rechenintensive numerische Simulationen, wie etwa Wettervorhersagen, Strömungssimulationen, Stabilitätsberechnungen, uvm.
Inhalt
- Aus alt mach neu
- Do’s and Don’ts in Fortran
- Don’t use common block and equivalent statement
- Avoid using GOTO
- Avoid SAVE attributes
- Use implicit none
- Make use of derived data types and classes
- Use the module system
- Don’t rely on short-circuit evaluation
- Quelle
- Autor
Aus alt mach neu
Fortran gilt als die erste jemals realisierte höhere Programmiersprache und wurde in den Jahren 1954 – 1957 von IBM entwickelt (FORTRAN I). Der Umfang der Sprache war noch sehr eingeschränkt, zum Beispiel gab es nur Integer (Ganzzahlen) und Reals (Gleitkommazahlen) als Datentypen und noch keine Funktionen. In den folgenden Jahren wurden neue verbesserte und umfangreichere Fortran Versionen entwickelt (FORTRAN II, FORTRAN III, FORTAN 66). Das nächste große Update bekam Fortran im Jahr 1977 (FORTRAN 77). Durch neue Features in der Sprache wurde diese Version sehr populär und damit schnell zu “dem” Fortran. Auch heute noch ist, wenn über Fortran Code gesprochen wird, hauptsächlich FORTRAN 77 Code gemeint, was auch die vielen Vorurteile gegenüber der Sprache erklärt. Seitdem gab es noch einige Updates der Sprache, die sie an moderne Programmierkonzepte und Standards heranführen.
Große Meilensteine in der Entwicklung waren die Updates zu Fortran 90 und Fortran 2003, welche neben der Namensänderung (FORTRAN → Fortran) gängige Konzepte wie unter anderem freie Sourcefile Formate, Module, Operator Overloading, Derived data types, Pointers und objektorientiertes Programmieren zur Programmiersprache hinzufügten. Zusätzlich dazu gab es mit Fortran 95 und Fortran 2008 jeweils ein kleines Update der Sprache. Die aktuellste Version des Fortran Standards ist Fortran 2018, wobei noch kein Compilerhersteller alle Features unterstützt.
Info
Höhere Programmiersprachen und Compiler
Mikroprozessoren werden einer sogenannten Maschinensprache programmiert, also ein Binärcode der vom Mikroprozessor als Folge von Befehlen (Instructions) interpretretiert wird. Da diese Sprachen sehr von der verwendeten Hardware abhängen und das direkte Programmamieren in einer Maschinensprache sehr aufwändig ist, war die Entwicklung von höheren Programmiersprachen und Compilern ein großer Fortschritt. Höhrere Programmiersprachen verwenden mathematische Ausdrücke oder Ausdrücke die an eine natürliche Sprache (meist Englisch) angelehnt sind die mittels einem Compiler (und Linker) in Maschinensprache übersetzt werden. Höhere Programmiersprachen sind unabhäng von der Hardware, die Anpassung an die konkrete Hardware wird vom Compiler übernommen.
Operator Overloading
ist eine Programmiertechnik mit der die Bedeutung von Operatoren (wie etwa +, -, *, …) von den jeweiligen verwendeten Typen abhängt. Beispielsweise liefert 1 + 2 die Zahl 3, aber “Helllo ” + “World” den String “Hello World”.
Derived Data Type
erlauben es dem Benutzer selbst Datentypen aus bestehenden Typen zu definieren. Dies bietet somit die Möglichkeit für die Definition von logisch zusammengehörenden Daten in einem Typ und die Wiederverwednung derer in verschiedenen Teilen des Programms.
Pointer
ist ein Datentyp, der die Speicheradresse einer Variable speichert anstatt die Variable selbst. Pointer verweisen (referenzieren) sozusagen auf eine Speicheradresse, das Extrahieren des dahinterliegenden Wertes wird als Dereferenzierung bezeichnet. Im Unterschied zu Pointern in C/C++ besitzen Fortran Pointer noch weitere Informationen und erlauben auch auf nicht zusammenhangende Speicherbereiche (im Fall von Arrays) zu verweisen.
Objektorientiertes Programmieren
ist eine Programmierstil in dem Daten nicht nur in Derived Data Types gesammelt werden sondern zusammen mit Logik und Funktionalität in soganannten Objekten gekapselt. Jedes Objekt hat definierte Schnittstellen über jene es mit anderen Objekten interagieren kann, z.B. sind oft nicht alle Daten und Funktionen eines Objekts für andere Objekte sichtbar. Ziel ist es Codeduplikationen zu vermeiden um das Fehlerpotential und den Wartungaufwand zu verringern.
Do’s & Don’ts in Fortran
Durch die lange Entwicklungsgeschichte von Fortran und um Kompatibilität mit Legacy Code zu wahren, gibt es zahlreiche veraltete und teils obskure Sprachfeatures in aktuellen Fortran Compilern. Eine umfassende Sammlung von “good and bad codingpractices” würde den Umfang dieses Artikel bei weitem sprengen. Dennoch möchten wir einige gängige Altlasten präsentieren, die sich in Legacy Code wiederfinden können, genauso wie ausgewählte Möglichkeiten, die neue Fortran Standards bieten. Für eine umfangreiche Liste von Do’s and Don’ts verweisen wir auf (1).
Don’t use common block and equivalent statement
In Fortran 77 und früher war es üblich, dass verschiedene Variablen auf dieselbe Speicheradresse verweisen, nämlich mittels dem common block und dem equivalent statement. Diese Ausdrücke wurden genutzt um Information zwischen Subroutinen zu teilen oder um (teuren) Speicherplatz für temporäre Variablen wiederzuverwenden. Mittlerweile sind diese Ausdrücke als obsolete deklariert und sollten nicht mehr verwendet werden. Um Daten zwischen Programmteilen auszutauschen sollten Module genutzt werden und Speicherplatz sollte bei Bedarf dynamisch allokiert und deallokiert werden.
Avoid using GOTO
Kein anderer Ausdruck ist so sehr mit Fortran verwurzelt wie das GOTO. In alten Programmen findest sich sehr oft eine exzessive Nutzung von GOTOs, teilweise auch geschuldet dem Mangel an alternativen Konstrukten. Über die Zeit haben sich verschiedene Varianten des GOTOs entwickelt, wie dem computed GOTO statement oder dem assigned GOTO statement. Auch gab es für die Handhabung von Schleifen oder IF-statements Varianten, die mit Sprüngen zu entsprechenden Labels arbeiten. In modernen Fortran Programmcodes sollten all diese Varianten der GOTOs wenn möglich vermieden werden und durch IF und SELECT Case (= switch) ersetzt werden. Eine nennenswerte Ausnahme für die Notwendigkeit von GOTOs in Fortran Code ist das Fehlermanagement, da Exceptions in Fortran nicht existieren.
Avoid SAVE attributes
Das SAVE Attribut erlaubt es, dass Variablen bei wiederholten Funktionsaufrufen ihren Wert beibehalten und gerade in Verbindung mit Parallelisierung kann dies zu schwer zu findenden Bugs und Dataraces führen. Das SAVE Attribut kann gefahrlos bei Variablen verwendet werden, die immer denselben Wert bei jedem Funktionsaufruf besitzen um so etwas Performance zu gewinnen. In allen anderen Fällen sollte es gemieden werden. Eine besonders hinterhältiges “Feature” von Fortran ist es, dass alle Variablen, die bei ihrer Deklaration gleich initialisiert werden automatisch ein implizites SAVE Attribut erhalten.
Use implicit none
Ein Konzept aus alten Fortran Standards war, dass undeklarierte Variablen automatisch als REAL deklariert werden, außer jene die mit dem Buchstaben i, j, k, l, m oder n beginnen werden als INTEGER deklariert. Dieses Konzept ist sehr fehleranfällig, vor allem deshalb weil der Compiler keine Fehlermeldung ausgibt wenn nicht deklarierte Variablen verwendet werden, z.B. durch einen Tippfehler. Durch dieses Konzept hat sich folgender Witz über Fortran eingebürgert: Fortran einzige Sprache wo gilt “God is Real”. Diese automatische Variablendeklaration kann mithilfe von dem IMPLICIT NONE für den aktuellen Bereich deaktiviert werden und es ist “good practice” diese im ganzen Programmcode umzusetzen.
Make use of derived data types and classes
Mit Fortran 90 wurde begonnen die Sprache Richtung objekt-orientierter Programmierung weiter zu entwickeln. Mit diesem Standard wurden user-defined datatypes eingefügt, die es erstmals erlaubten wiederverwendbare Strukturen von logisch zusammengehörenden Daten zu verwenden. Ebenso wurde das Konzept von generics hinzugefügt, sodass derselbe Funktionsname mit verschiedene Typen verwendet werden kann (es muss aber trotzdem die Funktion für jeden Typ programmiert werden). Gerade mit dem Fortran 2003 Standard wurde nochmal das objektorientiertes Programmieren forciert. Spätestens seit dessen sollte versucht werden, Daten und Logik in sinnvolle Klassen zu kapseln und Programmteile über wohldefinierte Interface interagieren zu lassen.
Use the module system
Fortran 90 leitete auch eine neue Form einer Programmorganisation ein, nämlich das Modulesystem. Ein Module besteht aus einer Menge von Deklarationen von Daten und Funktionen und Funktionsschnittstellen, die dann in andere Programmteile verwendet und sichtbar gemacht werden können. Zusätzlich bieten Module die Möglichkeit den Zugriff der beinhalteten Funktionen/Daten mittel PRIVATE/PUBLIC einzuschränken. Seit dem Fortran 2008 Standard gibt es sogenannte submodules, die es nun dem Programmierer ermöglichen den Programmcode in ein separates Submodule auszulagern. Die Notwendigkeit für diese ist einerseits, sehr große und unübersichtliche Module zu vermeiden, die Schnittstelle des Moduls klar ersichtlich zu haben, als auch die Recompilationszeiten zu reduzieren.
Don’t rely on short-circuit evaluation
Sehr viele Programmiersprachen evaluieren bei einer logischen Kombination von zwei Ausdrücken nur den Ersten, wenn durch diesen das Ergebnis bereits feststeht. Diese Verfahren wird als short-circuit evaluation bezeichnet. In Fortran ist es aber dem Compiler überlassen, ob short-circuit evaluation verwendet wird, d.h. im Fortran Standard ist es nicht verboten und auch nicht vorgeschrieben. Ein typischer Anwendungsfall für die Notwendigkeit von short-circuit evaluation wäre die Abfrage:
IF (PRESENT(x) .AND. x >0) THEN dosomething with x END IF |
In Fortran gibt es die Möglichkeit von optionalen Argumenten, d.h Parameter einer Funktion, die nicht zwingend übergeben werden müssen. Mit Hilfe der Funktion PRESENT(x) kann überprüft werden, ob der Parameter x übergeben wurde. In dem Beispiel wird nach der Überprüfung eine Abfrage getätigt, ob x größer als 0 ist. Falls x nicht übergeben wird, dann würde durch short-circuit evaluation die Abfrage x>0 nicht mehr getätigt werden, da bereits die erste Bedingung nicht gültig ist. Durch die möglicherweise nicht erfolge short-circuit evaluation würde das Programm an dieser Stelle aber abstürzen. Die korrekte Schreibweise ist das Aufteilen auf zwei einzelne Bedingungen:
IF (PRESENT(x)) THEN IF(x >0) THEN dosomething with x END IF END IF |
Andere typische Fälle sind Abfragen ob ein Pointer einer Speicheradresse zugeordnet ist oder ob eine mathematische Operation mit den Eingabewerten erlaubt ist (Division, Wurzel, …) .
Quelle
(1) S. Chapman, Fortran for scientists & engineers, McGraw-Hill Education, 4. Edition, 2017
Kontakt
Autor
DI Dr. Christoph Hofer
Professional Software Engineer Unit Industrial Software Applications