In den vorhergehenden Artikeln (Teil 1 und Teil 2) wurde zunächst ein Überblick über die ILE-Konzepte, sowie ein Vergleich der verschiedenen Programmier-Konzepte gegeben. In diesem Artikel geht es um die verschiedenen Prozedur-Arten (in RPG). Des Weiteren wird die Quellenstruktur der unterschiedlichen Prozedur-Typen beschrieben.

In einer RPG-IV-Teildatei können sowohl der Quellcode für Programme, als auch Prozeduren hinterlegt werden. An dieser Stelle wird zwischen Main- und (Sub-)Procedures unterschieden. In einer Teildatei können die folgenden Prozedurarten enthalten sein:

  • Main-Procedure,
  • Main-Procedure und (Sub-)Procedures sowie
  • nur interne und exportierte Procedures.

Main Procedure

Klassische RPG-Programme bestehen lediglich aus einer Main-Procedure oder anders ausgedrückt, die Main-Procedure entspricht der OPM-Programmstruktur. In der Main-Procedure können demnach die Bestimmungsarten H, F, D, (I), C und (O) bzw. die entsprechenden Free-Format-Äquivalente codiert werden.

Main-Procedures unterliegen dem RPG-Zyklus, der unter anderem die in den F-Bestimmungen hinterlegten Dateien automatisch öffnet und die Initialisierungs-Subroutine aktiviert. Man spricht in diesem Zusammenhang auch von zyklischen Main-Procedures zur Unterscheidung von linearen Main-Procedures, die mit Release 6.1 eingeführt wurden.

Eine lineare Main-Procedure wird wie eine Prozedur codiert und unterliegt nicht dem RPG-Zyklus, was unter anderem zur Folge hat, dass Programme mit linearer Main-Procedure rekursiv aufgerufen werden können. Eine Main-Procedure, unabhängig ob es sich um eine zyklische oder lineare Main-Prozedur handelt, stellt den Program-Entry-Point (PEP) für ein Programm dar, das heißt, die Main-Procedure kann direkt von einer Befehlszeile, aus einem Menü, aus einem anderen Programm oder auch aus einer anderen Prozedur aus aufgerufen werden.

Quelle: Birgitta Hauser

Tabelle 1.

Die Tabelle 1 zeigt die in einer klassischen Main-Procedure zulässigen Bestimmungsarten. Dabei ist folgendes anzumerken: Die Reihenfolge von F- und D-Bestimmungen ist nicht länger fix vorgegeben. Vielmehr können F- und D-Bestimmungen seit Release 7.1 TR 7 beliebig gemischt werden. Dabei spielt es keine Rolle, ob die Codierung im fixen oder Free-Format erfolgt.

Innerhalb der globalen D-Bestimmung sollten der Prototyp und das Procedure Interface hinterlegt werden. Die Prototypen von Programmen und exportierten Prozeduren sollten niemals hartcodiert, sondern immer über eine Copy-Strecke eingebunden werden. Während bei der Verwendung von Parameter-Listen (*ENTRY PLIST und CALL mit Parametern) ein Parameter-Versatz erst zur Laufzeit festgestellt werden kann, werden beim Prototyping die Parameter bereits zur Compile-Zeit geprüft. Die Prüfung erfolgt durch den Abgleich der Prototypen mit den beim Aufruf übergebenen Parametern bzw. deren Definitionen. Bei abweichenden Parameter-Definitionen wird das Programm/Modul nicht erstellt.

Dazu kommt noch, dass sowohl Parameter-Listen als auch die Call-Befehle CALL und CALLB im RPG-Free-Format nicht unterstützt werden. Der einzige im Free-Format zulässige CALL-Befehl ist CALLP (= Call a Prototyped Procedure or Program).

Quelle: Birgitta Hauser

Bild 1.

Das Beispiel in Bild 1 zeigt das Gerüst für eine Main-Procedure: Im Quellcode, genauer gesagt in den H-Bestimmungen, ist hinterlegt, dass das Programm nicht in der Default-Aktivierungsgruppe, sondern in der Aktivierungsgruppe MYPGM ausgeführt werden soll. Diese Angaben überschreiben die entsprechenden Optionen im Compile-Befehl CRTBNDRPG (RPG-Binderprogramm erstellen). Die Angaben sind durch Compiler-Direktiven bedingt und werden nur verwendet, wenn die Kompilierung über den CL-Befehl CRTBNDRPG erfolgt. Erfolgt eine zweistufige Kompilierung, das heißt, wird das Modul mit dem CL-Befehl CRTRPGMOD (RPG-Modul erstellen) erstellt und anschließend über den CL-Befehl CRTPGM (Programm erstellen) eingebunden, werden diese Angaben ignoriert.

Ebenso ist in den H-Bestimmungen eine Reihe von Binder-Verzeichnissen hinterlegt, in denen sich die zu bindenden Module und Serviceprogramme befinden. Binder-Verzeichnisse werden zur Compile-Zeit durchforstet, um die aufgerufenen Prozeduren zu lokalisieren und die entsprechenden Module oder Signaturen der Serviceprogramme in die (Service-)Programmobjekte zu integrieren.

Die Prototypen der aufgerufenen Prozeduren werden über Copy-Strecken in die Quelle integriert. In diesem Beispiel wird die Compiler-Direktive /INCLUDE verwendet. Alternativ kann auch die Compiler-Direktive /COPY verwendet werden. Der Unterschied zwischen /COPY und /INCLUDE liegt darin, wie der SQL-Precompiler embedded SQL die Copy-Strecke behandelt. Mit /INCLUDE eingebundene Copy-Strecken werden ignoriert mit /COPY eingebundene Copy-Strecken werden vom SQL-Precompiler aufgelöst. In Quellen ohne embedded SQL macht es keinen Unterschied ob die Copy-Strecken mit /COPY oder /INCLUDE eingebunden werden.

Das Procedure Interface (Ersatz für die *ENTRY PLIST) wird in den globalen D-Bestimmungen hinterelegt. Anschließend werden die globalen D-Bestimmungen definiert und die C-Bestimmungen wie gewohnt programmiert. Subroutinen sind auch weiterhin zulässig. Bei einer echten modularen Programmierung wird man feststellen, dass Sub-Routinen nicht mehr benötigt werden. Da Programme mit zyklischen Main-Procedures dem Zyklus unterliegen, muss die Main-Procedure entweder mit *INLR oder RETURN beendet werden.

(Sub-)Procedures

Im Anschluss an die Main-Procedure können beliebig viele Prozeduren codiert werden. In Quellen, in denen Prozeduren codiert werden, ist eine zyklische Main-Procedure nicht zwingend erforderlich. Die erste Prozedur wird in solchen Fällen unmittelbar nach den globalen D-Bestimmungen codiert.

Um zu verhindern, dass der Compiler versucht den RPG-Zyklus zu integrieren, muss in den H-Bestimmungen entweder das Schlüsselwort NoMain (sofern die Quelle nur interne und exportierte Prozeduren enthält) oder das Schlüsselwort Main (wenn eine lineare Main-Procedure verwendet wird) angegeben werden.

Im Gegensatz zu der (zyklischen) Main-Procedure unterliegen (Sub-)Procedures nicht dem RPG-Zyklus und können somit rekursiv aufgerufen werden. Prozeduren beginnen und enden immer mit einem P-Statement. Werden die neueren Free-Format-Operations-Codes verwendet, so beginnt eine Procedure mit dem Operations-Code DCL-PROC und endet mit dem Operations-Code END-PROC. Innerhalb der Prozedur können die Bestimmungsarten F (seit Release 6.1), D und C codiert werden.

Quelle: Birgitta Hauser

Bild 2.

Das Beispiel im Bild 2 zeigt das Format der P-Bestimmungen sowie den Beginn der internen Prozedur IntFirstProc. Unmittelbar nach der Bestimmungsart P kann der Prozedurname zwischen Position 7 und Position 21 angegeben werden. Bei längeren Prozedurnamen kann über Position 21 fortgeschrieben und können die übrigen Angaben in der nächsten Zeile angegeben werden. In diesem Fall muss der Umbruch jedoch durch drei aufeinanderfolgende Punkte (…) gekennzeichnet werden.

Bei dem beginnenden P-Statement ist die Angabe des Prozedurnamens zwingend erforderlich, bei dem beendenden P-Statement ist die Angabe des Prozedurnamens optional. Auf Position 25 wird angegeben, ob es sich um ein beginnendes (B=Beginn) oder beendendes (E=End) P-Statement handelt. Ab Position 44 können Schlüsselworte angegeben werden. Wird das Schlüssel-Wort EXPORT angegeben handelt es sich um eine exportierte Prozedur, fehlt es handelt es sich um eine interne Prozedur.

Quelle: Birgitta Hauser

Bild 3.

Mit Release 7.1 und Technology Refresh 7 wurden für die fixen Bestimmungsarten H, D, F und P auch Free-Format-Alternativen bereitgestellt. Im Free-Format beginnt eine Prozedur mit dem Operations-Code DCL-PROC und endet mit dem Operations-Code END-PROC. Der Prozedurname wird jeweils unmittelbar nach dem Operations-Code angegeben, wobei die Angabe bei dem Ende-Operations-Code optional ist. Im Anschluss an den Prozedurnamen können Schlüsselworte) z.B. Export für exportierte Prozeduren) angegeben werden.

Das Beispiel in Bild 3 zeigt den Beginn und das Ende der internen Prozedur MyFirsFreeProc im Free-Format.

Die Tabelle 2 zeigt den Aufbau und die zulässigen Bestimmungsarten für zwei (Sub-) Procedures. Der Aufruf von Sub-Procedures erfolgt über den Operations-Code CALLP (= Call a Prototyped Procedure or Program). Wird im Free-Format codiert, so ist die Angabe des Operations-Codes CALLP nur erforderlich, wenn eine Erweiterung angegeben werden soll – zum Beispiel (E), um einen Fehler abzufangen.

Quelle: Birgitta Hauser

Tabelle 2.

Für Prozeduren kann auch ein Rückgabewert definiert werden, in diesem Fall spricht man von Funktionen. Funktionen werden genau wie reguläre Built-In-Funktionen aufgerufen, zum Beispiel in einem EVAL-Statement (im Free-Format optional), in einem Ausdruck, in einer IF-Anweisung oder als Parameterfeld bei einem Programm-/Prozeduraufruf etc.

(Sub-)Procedures können mit Ein-/Ausgabeparameter definiert und aufgerufen werden. Die Parameterdefinition muss über Prototyping erfolgen, das heißt, das Procedure-Interface muss in den lokalen D-Bestimmungen der (Sub-)Procedure hinterlegt werden. Für interne Prozeduren ist die Angabe des Prototypen seit Release 7.1 optional. Für exportierte Prozeduren ist die Definition des Prototypen zwingend erforderlich. Prototypen sollten immer in einer eigenen Teildatei hinterlegt werden, die in Form einer Copy-Strecke integriert wird.

Dateien, die in den lokalen F-Bestimmungen definiert wurden, und Variablen, die in den prozedurinternen D-Bestimmungen (lokal) definiert wurden, können nur innerhalb der Prozedur verwendet werden. Aus einer Prozedur kann jedoch auf alle Dateien und Variablen, die in den globalen F- beziehungsweise D-Bestimmungen definiert wurden, zugegriffen werden.

Wird die gleiche Variable einmal global und einmal lokal definiert, so kann innerhalb der Prozedur, in der die lokale Variable definiert wurde auch nur auf die lokale Variable zugegriffen werden. In anderen Prozeduren in der gleichen Teildatei, in denen die Variable nicht lokal definiert wurde, wird auf die globale Variable zugegriffen.

Da (Sub-)Procedures nicht dem RPG-Zyklus unterliegen, wird jedem Aufruf ein neues Variablen-Set zur Verfügung gestellt, das heißt, die lokalen Variablen sind/werden beim Aufruf immer initialisiert. Sofern jedoch der Inhalt von lokalen Variablen über mehrere Aufrufe hinweg gerettet werden soll, kann bei der Definition der entsprechenden Variable das Schlüsselwort STATIC angegeben werden.

Da (Sub-)Procedures nicht dem RPG-Zyklus unterliegen, werden für lokale F-Bestimmungen keine I/O-Strukturen generiert, was zur Folge hat, dass für die zu verarbeitenden Datensätze manuell Datenstrukturen angelegt werden müssen. Diese Datenstrukturen können entweder als externe Datenstrukturen mit Hilfe des Schlüsselworts EXTNAME oder basierend auf dem Dateiformat mit Hilfe des Schlüsselworts LIKEREC definiert werden. Diese Datenstrukturen müssen dann in Verbindung mit den Schreib-/Lese-Operations-Codes – zum Beispiel READ, CHAIN, UPDATE – angegeben werden, um den Datensatz aufzunehmen.

Die Codierung innerhalb der lokalen C-Bestimmungen unterscheidet sich nicht von der Codierung in Main-Procedures, das heißt, es können alle zulässigen Operations-Codes und Built-in-Funktionen, aber auch embedded SQL verwendet werden. Aus (Sub-)Procedures können nur andere (Sub-)Procedures oder Programme aufgerufen werden. Es ist jedoch nicht möglich aus einer (internen) Sub-Procedure eine Subroutine in der Main-Procedure aufzurufen.

Innerhalb von (Sub-)Procedures können – wie in der Main-Procedure auch – Subroutinen codiert werden. Subroutinen werden am Ende der prozedurinternen C-Bestimmungen vor dem beendenden P-Statement angegeben. Dies ist insbesondere am Anfang hilfreich, da auf diese Art und Weise auch etwas größere Prozeduren strukturiert werden können. Sobald man jedoch gelernt hat, richtig modular zu denken, wird man feststellen, dass Subroutinen tatsächlich so gut wie nicht mehr benötigt werden.

(Sub-)Procedures werden mit dem Operations-Code RETURN verlassen. Da der RPG-Zyklus jedoch nicht eingebunden wird, wird nicht geprüft, ob er in der (Sub-)Procedure auch wirklich angegeben wurde. Fehlt der OpCode RETURN, so wird die Prozedur nach Ausführung des letzten Statements beendet. Dennoch sollte man es sich zur Gewohnheit machen, den Operations-Code RETURN am Ende der Prozedur anzugeben. Dies ist insbesondere dann wichtig, wenn in der Prozedur noch Subroutinen codiert sind. Fehlt der RETURN-Operations-Code in diesem Szenario, wird das Prozedurende nie erreicht.

Birgitta Hauser

Midrange Techknowletter