Dieses Dokument behandelt die Objekttypen, die als Routinen, d.h. als untergeordnete Programme aufgerufen werden können.
Anmerkung:
Obwohl sie ebenfalls von anderen Objekten aufgerufen werden, sind
Helproutinen und Maps (Masken) genau genommen keine Routinen. Sie werden
deshalb in getrennten Dokumenten beschrieben; siehe Abschnitt
Helproutine und
Map.
Folgende Themen werden behandelt:
Verarbeitungsfluss beim Aufruf eines untergeordneten Programms
Vergleich zwischen externer Subroutine, Subprogramm und Function
Eine typische Natural-Anwendung besteht nicht aus einem einzigen großen Programm, sondern ist in mehrere Objekt-Module aufgeteilt. Jedes dieser Objekte stellt eine funktionale Einheit von überschaubarer Größe dar, und jedes Objekt ist mit den anderen Objekten der Anwendung auf eine klar definierte Weise verbunden. Dadurch ergibt sich eine übersichtlich strukturierte Anwendung, was die Entwicklung und spätere Wartung erheblich erleichtert und beschleunigt.
Ein Hauptprogramm, das ausgeführt wird, kann andere Programme, Subprogramme, Subroutinen, Helproutinen und Maps aufrufen. Diese Objekte können ihrerseits wiederum andere Objekte aufrufen (eine Subroutine kann beispielsweise eine andere Subroutine aufrufen). Dadurch kann die objekt-orientierte Struktur einer Anwendung äußerst komplex und vielschichtig werden.
Ein aufgerufenes Objekt ist jeweils eine Stufe (Level) unter dem Objekt, von dem es aufgerufen wurde; d.h. bei jedem Aufruf eines untergeordneten Objekts erhöht sich die Stufennummer um 1.
Ein Programm, das selbständig ausgeführt wird, wird auf Stufe 1 eingeordnet; Subprogramme, Subroutinen, Maps oder Helproutinen, die direkt von diesem Hauptprogramm aufgerufen werden, sind auf Stufe 2; ruft eine dieser Subroutinen ihrerseits eine andere Subroutine auf, so ist letztere auf Stufe 3.
Wird aus einem anderen Objekt über ein FETCH
-Statement ein Programm
aufgerufen, so wird es als Hauptprogramm eingestuft und auf Stufe 1
eingeordnet. Ein Programm, das mit FETCH RETURN
aufgerufen
wird, wird dagegen als untergeordnetes Programm eingestuft und ist eine Stufe
unter dem Objekt, von dem es aufgerufen wurde.
Die folgende Abbildung enthält ein Beispiel für mehrere Stufen aufgerufener Objekte und zeigt, wie diese Stufen gezählt werden:
Sie können die Systemvariable *LEVEL
benutzen (siehe Systemvariablen-Dokumentation),
um die Level-Nummer des Objekts zu erfahren, das gerade ausgeführt wird.
Wenn ein CALLNAT
-,
PERFORM
- oder
FETCH
RETURN
-Statement oder ein Function Call, das eine
untergeordnete Routine — ein Subprogramm, eine externe Subroutine, ein Programm
bzw. eine Function — aufruft, ausgeführt wird, wird die Ausführung des
aufrufenden Objekts unterbrochen, und die Ausführung der untergeordneten
Routine beginnt.
Die Ausführung der untergeordneten Routine wird solange fortgesetzt, bis
entweder ihr END
-Statement erreicht ist oder die
Verarbeitung der untergeordneten Routine durch die Ausführung eines
ESCAPE
ROUTINE
- oder ESCAPE
MODULE
-Statements gestoppt wird.
In beiden Fällen wird die Verarbeitung des aufrufenden Objekts mit dem
nächsten Statement nach dem CALLNAT
-, PERFORM
- bzw.
FETCH RETURN
-Statement, mit dem die untergeordnete Routine
aufgerufen wurde, fortgesetzt.
Im Falle eines Function Call wird die Verarbeitung des aufrufenden Objekts mit dem Statement fortgesetzt, das den Function Call enthält.
Ein Programm kann selbständig ausgeführt — und getestet — werden.
Um ein Source-Programm zu katalogisieren (kompilieren) und
anschließend auszuführen, verwenden Sie das Systemkommando
RUN
.
Um ein Programm auszuführen, das bereits als katalogisiertes Objekt
existiert, verwenden Sie das Systemkommando
EXECUTE
.
Ein Programm kann auch von einem anderen Objekt mit einem
FETCH
- oder
FETCH
RETURN
-Statement aufgerufen werden. Das aufrufende Objekt
kann ein anderes Programm, eine Subroutine, ein
Subprogramm, eine
Function, eine
Helproutine oder eine
Verarbeitungsregel in einer Map sein.
Wenn ein Programm mit FETCH RETURN
-Statement aufgerufen
wird, wird die Ausführung des aufrufenden Objekts unterbrochen — nicht beendet
—, und das aufgerufene Programm wird als untergeordnetes Programm
aktiviert. Wenn die Ausführung des aufgerufenen Programms beendet ist, wird das
aufrufende Objekt reaktiviert und seine Ausführung mit dem nächsten Statement
nach dem FETCH RETURN
-Statement fortgesetzt.
Wenn ein Programm mit FETCH
aufgerufen wird, wird die
Ausführung des aufrufenden Objekts beendet, und das aufgerufene Programm wird
als Hauptprogramm aktiviert. Das aufrufende Objekt wird nach beendeter
Ausführung des aufgerufenen Programms nicht reaktiviert.
Die folgenden Themen werden nachfolgend erörtert:
Ein mit FETCH
RETURN
aufgerufenes Programm kann auf die vom aufrufenden
Objekt benutzte Global Data
Area (GDA) zugreifen.
Darüber hinaus kann jedes Programm seine eigene Local Data Area (LDA) haben, in der die nur in diesem Programm verwendeten Felder definiert sind. Außerdem kann ein Programm auf anwendungsunabhängige Variable (AIVs) zugreifen. Weitere Informationen siehe Definition von anwendungsunabhängigen Variablen in der Statements-Dokumentation.
Ein mit FETCH RETURN
aufgerufenes Programm kann jedoch
keine eigene Global Data Area (GDA) haben.
Ein mit FETCH
als Hauptprogramm aufgerufenes Programm
verwendet in der Regel seine eigene Global Data Area (wie in der obigen
Abbildung gezeigt). Es könnte allerdings auch dieselbe Global Data Area
verwenden wie das aufrufende Objekt.
Anmerkung:
Ein Source-Programm kann auch mit einem RUN
-Statement
aufgerufen werden; siehe RUN
-Statement im der
Statements-Dokumentation.
In der Regel wird eine Subroutine verwendet, um Funktionalität zu implementieren, die von verschiedenen Objekten in einer Anwendung benutzt wird.
Die Statements, aus denen eine Subroutine besteht, müssen innerhalb
eines DEFINE
SUBROUTINE
... END−SUBROUTINE
-Statement-Blocks
definiert werden.
Eine Subroutine wird mit einem PERFORM
-Statement
aufgerufen.
Eine Subroutine kann eine interne Subroutine ("inline subroutine") oder eine externe Subroutine sein:
Interne Subroutine
Eine interne Subroutine wird innerhalb des Objekts, welches das sie
aufrufende PERFORM
-Statement enthält, definiert.
Externe Subroutine
Eine externe Subroutine wird als separates Objekt — vom Typ Subroutine
— außerhalb des Objektes, welches sie aufruft, definiert.
Falls Sie einen Code-Block haben, der innerhalb eines Objekts mehrmals
ausgeführt werden soll, ist es sinnvoll, eine interne Subroutine zu verwenden.
Sie müssen diesen Block dann nur einmal innerhalb eines DEFINE
SUBROUTINE
-Statement-Blocks kodieren, und rufen ihn dann mit mehreren
PERFORM
-Statements auf.
DieseThemen werden nachfolgend erörtert:
Eine interne Subroutine kann in einem Objekt vom Typ Programm, Function, Subprogramm, Subroutine oder Helproutine enthalten sein.
Eine interne Subroutine hat Zugriff auf alle Datenfelder innerhalb des Objekts, in dem sie enthalten ist.
Eine externe Subroutine — also ein Objekt des Typs Subroutine — kann nicht selbständig ausgeführt werden. Sie muss von einem anderen Objekt aufgerufen werden. Das aufrufende Objekt kann ein Programm, eine Function, ein Subprogramm, eine Subroutine, eine Helproutine oder eine Verarbeitungsregel in einer Map sein.
Eine externe Subroutine kann auf die Global Data Area (GDA) zugreifen, die vom aufrufenden Objekt benutzt wird.
Darüber hinaus können mit dem PERFORM
-Statement Parameter von
dem aufrufenden Objekt an die externe Subroutine übergeben werden. Diese
Parameter müssen entweder im DEFINE
DATA PARAMETER
-Statement der Subroutine oder in einer von der
Subroutine benutzen Parameter Data
Area (PDA) definiert werden.
Außerdem kann eine externe Subroutine eine eigene Local Data Area (LDA) haben, in der die Felder definiert sind, die nur innerhalb der Subroutine verwendet werden sollen. Eine externe Subroutine kann jedoch keine eigene Global Data Area (GDA) haben.
Außerdem kann eine externe Subroutine auf anwendungsunabhängige Variable (AIVs) zugreifen. Weitere Informationen siehe Definition von anwendungsunabhängigen Variablen in der Statements-Dokumentation.
In der Regel wird ein Subprogramm verwendet, um Funktionalität zu implementieren, die von verschiedenen Objekten in einer Anwendung benutzt werden.
Ein Subprogramm kann nicht selbständig ausgeführt werden. Es muss von einem anderen Objekt aufgerufen werden. Das aufrufende Objekt kann ein Programm, eine Function, ein Subprogramm, eine Subroutine oder eine Helproutine sein.
Ein Subprogramm wird mit einem CALLNAT
-Statement
aufgerufen.
Wenn das CALLNAT
-Statement ausgeführt wird, wird die
Ausführung des aufrufenden Objekts unterbrochen und das Subprogramm ausgeführt.
Nach der Ausführung des Subprogramms wird die Ausführung des aufrufenden
Objekts mit dem nächsten Statement nach dem CALLNAT
-Statement
fortgesetzt.
Mit dem CALLNAT
-Statement können
Parameter von dem aufrufenden Objekt an das Subprogramm übergeben werden. Diese
Parameter sind die einzigen Daten, die dem Subprogramm vom aufrufenden Objekt
zur Verfügung stehen. Sie müssen entweder im DEFINE DATA
PARAMETER
-Statement des Subprogramms oder in einer vom
Subprogramm benutzen Parameter Data
Area (PDA) definiert werden.
Außerdem kann ein Subprogramm eine eigene Local Data Area (LDA) haben, in der die Felder definiert sind, die innerhalb des Subprogramms verwendet werden sollen.
Wenn ein Subprogramm seinerseits eine Subroutine oder Helproutine aufruft, kann es eine eigene Global Data Area (GDA) haben und diese gemeinsam mit der Subroutine bzw. Helproutine nutzen.
Außerdem kann ein Subprogramm auf anwendungsunabhängige Variable (AIVs) zugreifen. Weitere Informationen siehe Definition von anwendungsunabhängigen Variablen in der Statements-Dokumentation.
In der Regel wird eine Function verwendet, um Funktionalität zu implementieren, die von verschiedenen Objekten in einer Anwendung benutzt werden.
Im Unterschied zu den in Natural eingebauten Systemfunktionen dient eine Function dazu, benutzerdefinierte Funktionalität zur Verfügung zu stellen.
Die Function liefert einen Ergebniswert zurück, der von dem aufrufenden Objekt verwendet wird. Der Ergebniswert wird anhand der Daten errechnet, die der Function zur Verfügung stehen.
Ein Objekt des Typs Function enthält ein
DEFINE
FUNCTION
-Statement für die Definition einer einzelnen
Funktion und das zugehörige END
-Statement.
Die Function selbst wird durch einen Function Call aufgerufen.
Mit dem Function Call können Parameter vom aufrufenden Objekt an die
Function übergeben werden. Diese Parameter sind die einzigen Daten, die der
Function vom aufrufenden Objekt zur Verfügung stehen. Sie müssen im
DEFINE
FUNCTION
-Statement definiert werden.
Zusätzlich kann eine Function eine eigene Local Data Area (LDA) haben, in der die Felder definiert sind, die innerhalb der Function verwendet werden sollen. Eine Function kann jedoch keine eigene Global Data Area (GDA) haben.
Außerdem kann eine Function auf anwendungsunabhängige Variablen (AIVs) zugreifen. Weitere Informationen siehe Definition von anwendungsunabhängigen Variablen in der Statements-Dokumentation.
Falls erforderlich, können Sie Ergebnis- und Parameterlayouts für das
Objekt definieren, das die Function aufruft. Dazu dient das
DEFINE
PROTOTYPE
-Statement.
Weitere Informationen siehe Function Call.
In diesem Abschnitt werden die Merkmale der externen Subroutine, des Subprogramms und der Function zusammengefasst und miteinander verglichen.
Folgende Merkmale sind bei allen vorhanden:
Der Programmcode, der die Logik der Routine bildet, befindet sich in einem separaten Objekt, das in einer Natural-Library gespeichert wird.
Parameter werden in dem Objekt mittels eines DEFINE DATA
PARAMETER
-Statement definiert.
Die Unterschiede zwischen externer Subroutine, Subprogramm und Function werden in der folgenden Tabelle dargestellt:
Betrachtungsgegenstand | Externe Subroutine | Subprogramm | Function |
---|---|---|---|
Maximale Länge des Namens | 32 Zeichen | 8 Zeichen | 32 Zeichen |
Verwendung einer Global Data Area (GDA) | Nutzt eine GDA gemeinsam mit dem Aufrufer. | Legt eine Instanz einer GDA an. | Eine GDA ist nicht zulässig. |
Prüfung von Format/Länge der übergebenen Paramter gegen die Definition im aufgerufen Objekt bei der Kompilierung | Prüfung erfolgt nur, wenn die Compiler Option
PCHECK auf
ON gesetzt ist.
|
Prüfung erfolgt nur, wenn die Compiler Option
PCHECK auf
ON gesetzt ist.
|
Prüfung erfolgt nur, wenn zur Kompilierungszeit ein katalogisiertes Function-Objekt existiert. |
Aufruf durch | Aufruf durch PERFORM -Statement
|
Aufruf durch CALLNAT -Statement
|
Aufruf durch einen
Function Call
Ein Function Call kann in Statements anstelle von schreibgeschützten Operanden benutzt werden. Ein Function Call kann auch als Statement benutzt werden. |
Bestimmung des aufzurufenden Objekts zur Kompilierungs-/Laufzeit | Bestimmung zur Kompilierungszeit | Bestimmung zur Kompilierungs- bzw.
Ausführungszeit, je nach dem Operanden, der beim
CALLNAT -Statement
verwendet wird.
|
Bestimmung zur Kompilierungs- bzw. Ausführungszeit, je nach dem Operanden, der beim Function Call verwendet wird. |
Nutzung eines Ergebniswertes in einem Statement | Ein Ergebniswert muss einem Parameter zugewiesen werden, der als Operand in einem Statement verwendet werden soll. | Ein Ergebniswert muss einem Parameter zugewiesen werden, der als Operand in einem Statement verwendet werden soll. | Das Ergebnis eines Function Call wird als Operand in dem Statement verwendet, das den Function Call enthält. |
In den nachfolgenden Beispielen erfolgt ein Vergleich zwischen einem Function Call und dem Aufruf eines Subprogramms.
Das folgende Beispiel zeigt ein Programm, das eine Function aufruft,
und die Definition der Function, die mit einem
DEFINE FUNCTION
erstellt wird.
** Example 'FUNCAX01': Calling a function (Program) ************************************************************************ * WRITE 'Function call' F#ADD(< 2,3 >) /* Function call. /* No temporary variables needed. * END
** Example 'FUNCAX02': Calling a function (Function) ************************************************************************ DEFINE FUNCTION F#ADD RETURNS #RESULT (I4) DEFINE DATA PARAMETER 1 #SUMMAND1 (I4) BY VALUE 1 #SUMMAND2 (I4) BY VALUE END-DEFINE /* #RESULT := #SUMMAND1 + #SUMMAND2 /* END-FUNCTION * END
Um die Funktionalität, die im obgigen Beispiel für den Function Call gezeigt wird, stattdessen mittels eines Subprogramm-Aufrufs zu implementieren, müssen Sie temporäre Variablen angeben.
Das folgende Beispiel zeigt ein Programm, das ein Subprogramm unter Verwendung einer temporären Variablen aufruft.
** Example 'FUNCAX03': Calling a subprogram (Program) ************************************************************************ DEFINE DATA LOCAL 1 #RESULT (I4) INIT <0> END-DEFINE * CALLNAT 'FUNCAX04' #RESULT 2 3 /* Result is stored in #RESULT. * WRITE '=' #RESULT /* Print out the result of the /* subprogram. * END
** Example 'FUNCAX04': Calling a subprogram (Subprogram) ************************************************************************ DEFINE DATA PARAMETER 1 #RESULT (I4) BY VALUE RESULT 1 #SUMMAND1 (I4) BY VALUE 1 #SUMMAND2 (I4) BY VALUE END-DEFINE * #RESULT := #SUMMAND1 + #SUMMAND2 * END