Subroutines in SAP ABAP
Local Data in the Subroutine Locate the document in its SAP
Library structure
Data declarations in procedures create local data types and
objects that are only visible within that procedure. There are two kinds of
data types and objects – dynamic and static. Dynamic data objects only exist
while the subroutine is running, while static objects still exist after the
subroutine has finished running, and retain their values until the next time
the subroutine is called. Field symbols can be declared locally. You can also
use a special kind of data object for subroutines – copies of global data on a
local data stack. You define and address them using field symbols.
Dynamic Local Data Types and Objects
Local data types and objects declared in subroutines using
the TYPES and DATA statements are deleted when the subroutine ends, and
recreated each time the routine is called.
Every subroutine has its own local naming space. If you
declare a local data type or object with the same name as a global data type or
object, the global type or object cannot be addressed from within the
subroutine. Local data types or data objects hide identically named global data
types or objects. This means that if you use the name of a data type or object
in the subroutine, you always address a locally declared object – if this
exists – and otherwise a globally declared one. To avoid this, you must assign
other names to local types and objects. For example, you might name all of your
local data starting with ‘L_’.
Example
REPORT demo_mod_tech_data_types .
TYPES word(10) TYPE c.
DATA text TYPE word.
text = '1234567890'. WRITE / text.
PERFORM datatest.
WRITE / text.
FORM datatest.
TYPES word(5) TYPE
c.
DATA text TYPE word.
text = 'ABCDEFGHJK'.
WRITE / text.
ENDFORM.
When you run the program, the following is displayed:
1234567890
ABCDE
1234567890
In this example, a data type WORD and a data object TEXT
with type WORD are declared globally in the main program. After assigning a
value to TEXT and writing it to the list, the internal subroutine DATATEST is
called. Inside the subroutine, a data type WORD and a data object TEXT with
type WORD are declared locally. They hide the global type and object. Only
after exiting the subroutine the global definitions are valid again.
Static Local Data Objects
If you want to keep the value of a local data object after
exiting the subroutine, you must use the STATICS statement to declare it
instead of the DATA statement. With STATICS you declare a data object that is
globally defined, but only locally visible from the subroutine in which it is
defined.
Example
REPORT demo_mod_tech_statics.
PERFORM datatest1.
PERFORM datatest1.
SKIP.
PERFORM datatest2.
PERFORM datatest2.
FORM datatest1.
TYPES f_word(5) TYPE
c.
DATA f_text TYPE f_word VALUE 'INIT'.
WRITE f_text.
f_text = '12345'.
WRITE f_text.
ENDFORM.
FORM datatest2.
TYPES f_word(5) TYPE c.
STATICS f_text TYPE f_word VALUE 'INIT'.
WRITE f_text.
f_text = 'ABCDE'.
WRITE f_text.
ENDFORM.
When you run the program, the following is displayed:
INIT 12345 INIT 12345
INIT ABCDE ABCDE ABCDE
In this example, two similar subroutines DATATEST1 and
DATATEST2 are defined. In DATATEST2, the STATICS statement is used instead of
the DATA statement to declare the data object F_TEXT. During each call of
DATATEST1, F_TEXT is initialized again, but it keeps its value for DATATEST2.
The VALUE option of the STATICS statement functions only during the first call
of DATATEST2.
Global Data from the Main Program Locate the document in its
SAP Library structure
Subroutines can access all of the global data in the program
in which they are defined (main program). You therefore do not need to define a
parameter interface if you do not want to change any data in the subroutine, or
if very little data is involved.
Example
FORM HEADER.
WRITE: / 'Program
started by', SY-UNAME,
/ 'on host', SY-HOST,
'date:', SY-DATUM, 'time:', SY-UZEIT.
ULINE.
ENDFORM.
This example creates a subroutine called HEADER, which, like
the example of an include program, displays a list header.
However, if you want subroutines to perform complex
operations on data without affecting the global data in the program, you should
define a parameter interface through which you can pass exactly the data you
need. In the interests of good programming style and encapsulation, you should
always use a parameter interface, at least when the subroutine changes data.
Protecting Global Data Objects Against Changes
To prevent the value of a global data object from being
changed inside a subroutine, use the following statement:
LOCAL <f>.
This statement may only occur between the FORM and ENDFORM
statements. With LOCAL, you can preserve the values of global data objects
which cannot be hidden by a data declaration inside the subroutine.
For example, you cannot declare a table work area that is
defined by the TABLES statement with another TABLES statement inside a
subroutine. If you want to use the table work area locally, but preserve its
contents outside the subroutine, you must use the LOCAL statement.
Example
PROGRAM FORM_TEST.
TABLES SFLIGHT.
PERFORM TABTEST1.
WRITE: / SFLIGHT-PLANETYPE, SFLIGHT-PRICE.
PERFORM TABTEST2.
WRITE: / SFLIGHT-PLANETYPE, SFLIGHT-PRICE.
FORM TABTEST1.
SFLIGHT-PLANETYPE =
'A310'.
SFLIGHT-PRICE =
'150.00'.
WRITE: /
SFLIGHT-PLANETYPE, SFLIGHT-PRICE.
ENDFORM.
FORM TABTEST2.
LOCAL SFLIGHT.
SFLIGHT-PLANETYPE =
'B747'.
SFLIGHT-PRICE =
'500.00'.
WRITE: /
SFLIGHT-PLANETYPE, SFLIGHT-PRICE.
ENDFORM.
When you run the program, the following is displayed:
A310 150.00
A310 150.00
B747 500.00
A310 150.00
The program creates a table work area SFLIGHT for the
database table SFLIGHT. Different values are assigned to the table work area
SFLIGHT in TABTEST1 and TABTEST2. While the values assigned in TABTEST1 are
valid globally, the values assigned in TABTEST2 are only valid locally.
Defining Subroutines Locate the document in its SAP Library
structure
A subroutine is a block of code introduced by FORM and
concluded by ENDFORM.
FORM <subr> [USING
... [VALUE(]<pi>[)] [TYPE <t>|LIKE <f>]... ]
[CHANGING... [VALUE(]<pi>[)] [TYPE <t>|LIKE <f>]... ].
...
ENDFORM.
<subr> is the name of the subroutine. The optional
additions USING and CHANGING define the parameter interface. Like any other processing
block, subroutines cannot be nested. You should therefore place your subroutine
definitions at the end of the program, especially for executable programs (type
1). In this way, you eliminate the risk of accidentally ending an event block
in the wrong place by inserting a FORM...ENDFORM block.
Data Handling in Subroutines
Global Data from the Main Program
Local Data in the Subroutine
The Parameter Interface
Examples of Subroutines Locate the document in its SAP
Library structure
Example of Passing Parameters by Reference
Example
PROGRAM FORM_TEST.
DATA: NUM1 TYPE I,
NUM2 TYPE I,
SUM TYPE I.
NUM1 = 2. NUM2 = 4.
PERFORM ADDIT USING NUM1 NUM2 CHANGING SUM.
NUM1 = 7. NUM2 = 11.
PERFORM ADDIT USING NUM1 NUM2 CHANGING SUM.
FORM ADDIT
USING ADD_NUM1
ADD_NUM2
CHANGING
ADD_SUM.
ADD_SUM = ADD_NUM1 +
ADD_NUM2.
PERFORM OUT USING
ADD_NUM1 ADD_NUM2 ADD_SUM.
ENDFORM.
FORM OUT
USING OUT_NUM1
OUT_NUM2
OUT_SUM.
WRITE: / 'Sum of',
OUT_NUM1, 'and', OUT_NUM2, 'is', OUT_SUM.
ENDFORM.
The produces the following output:
Sum of 2 and 4 is 6
Sum of 7 and 11 is 18
In this example, the actual parameters NUM1, NUM2, and SUM
are passed by reference to the formal parameters of the subroutine ADDIT. After
changing ADD_SUM, the latter parameters are then passed to the formal
parameters OUT_NUM1, OUT_NUM2, and OUT_SUM of the subroutine OUT.
Input parameters which are changed in the subroutine are
also changed in the calling program. To prevent this, you must pass the
parameter by value in a USING addition.
Example of passing parameters by reference
Example
PROGRAM FORM_TEST.
DATA: NUM TYPE I VALUE 5,
FAC TYPE I VALUE 0.
PERFORM FACT USING NUM CHANGING FAC.
WRITE: / 'Factorial of', NUM, 'is', FAC.
FORM FACT
USING
VALUE(F_NUM)
CHANGING
F_FACT.
F_FACT = 1.
WHILE F_NUM GE 1.
F_FACT = F_FACT *
F_NUM.
F_NUM = F_NUM - 1.
ENDWHILE.
ENDFORM.
The produces the following output:
Factorial of 5 is 120
To ensure that an input parameter is not changed in the
calling program, even if it is changed in the subroutine, you can pass data to
a subroutine by value. In this example, the factorial of a number NUM is
calculated. The input parameter NUM is passed to the formal parameter F_NUM of
the subroutine. Although F_NUM is changed in the subroutine, the actual
parameter NUM keeps its old value. The output parameter FAC is passed by
reference.
Example of output parameters
Example
PROGRAM FORM_TEST.
DATA: OP1 TYPE I,
OP2 TYPE I,
RES TYPE I.
OP1 = 3.
OP2 = 4.
PERFORM MULTIP
USING OP1
OP2
CHANGING
RES.
WRITE: / 'After subroutine:',
/ 'RES=' UNDER 'RES=', RES.
FORM MULTIP
USING VALUE(O1)
VALUE(O2)
CHANGING VALUE(R).
R = O1 * O2.
WRITE: / 'Inside
subroutine:',
/ 'R=', R, 'RES=', RES.
ENDFORM.
The produces the following output:
Inside subroutine:
R= 12 RES= 0
After subroutine:
RES= 12
To return a changed formal parameter once the subroutine has
finished successfully, you can use a CHANGING parameter and pass the parameter
by reference. In this example, the actual parameters OP1 and OP2 are passed by
value in the USING addition to the formal parameters O1 and O2. The actual
parameter RES is passed by value to the formal parameter R using CHANGING. By
writing R and RES onto the screen from within the subroutine, it is
demonstrated that RES has not changed its contents before the ENDFORM statement.
After returning from the subroutine, its contents have changed.
Example of passing structures
Example
PROGRAM FORM_TEST.
TYPES: BEGIN OF LINE,
NAME(10) TYPE C,
AGE(2) TYPE N,
COUNTRY(3)
TYPE C,
END OF LINE.
DATA WHO TYPE LINE.
WHO-NAME = 'Karl'. WHO-AGE = '10'. WHO-COUNTRY = 'D'.
PERFORM COMPONENTS CHANGING WHO.
WRITE: / WHO-NAME, WHO-AGE, WHO-COUNTRY.
FORM COMPONENTS
CHANGING
VALUE(PERSON) TYPE LINE.
WRITE: /
PERSON-NAME, PERSON-AGE, PERSON-COUNTRY.
PERSON-NAME =
'Mickey'.
PERSON-AGE = '60'.
PERSON-COUNTRY =
'USA'.
ENDFORM.
The produces the following output:
Karl 10 D
MICKEY 60 USA
The actual parameter WHO with the user-defined, structured
data type LINE is passed to the formal parameter PERSON. The formal parameter
PERSON is typed with TYPE LINE. Since LINE is a user-defined data type, the
type of PERSON is completely specified. The subroutine accesses and changes the
components of PERSON. They are then returned to the components of WHO in the
calling program.
Example of passing internal tables
Example
PROGRAM FORM_TEST.
DATA: BEGIN OF LINE,
COL1 TYPE I,
COL2 TYPE I,
END OF LINE.
DATA ITAB LIKE STANDARD TABLE OF LINE.
PERFORM FILL CHANGING ITAB.
PERFORM OUT USING ITAB.
FORM FILL CHANGING F_ITAB LIKE ITAB.
DATA F_LINE LIKE
LINE OF F_ITAB.
DO 3 TIMES.
F_LINE-COL1 =
SY-INDEX.
F_LINE-COL2 =
SY-INDEX ** 2.
APPEND F_LINE TO
F_ITAB.
ENDDO.
ENDFORM.
FORM OUT USING VALUE(F_ITAB) LIKE ITAB.
DATA F_LINE LIKE
LINE OF F_ITAB.
LOOP AT F_ITAB INTO
F_LINE.
WRITE: /
F_LINE-COL1, F_LINE-COL2.
ENDLOOP.
ENDFORM.
The produces the following output:
1 1
2 4
3 9
You can define the types of the formal parameters of the
parameter interfaces of procedures as internal tables. In the example, the
subroutines FILL and OUT each have one formal parameter defined as an internal
table. An internal table without header line is passed to the subroutines. Each
subroutine declares a work area F_LINE as a local data object. Were ITAB a
table with a header line, you would have to replace ITAB with ITAB[] in the
PERFORM and FORM statements.
Example of the TABLES parameter
This example is provided for completeness. The TABLES
parameter is only supported for the sake of compatibility and should not be
used.
Example
PROGRAM FORM_TEST.
TYPES: BEGIN OF LINE,
COL1 TYPE I,
COL2 TYPE I,
END OF LINE.
DATA: ITAB TYPE STANDARD TABLE OF LINE WITH HEADER LINE,
JTAB TYPE
STANDARD TABLE OF LINE.
PERFORM FILL TABLES ITAB.
MOVE ITAB[] TO JTAB.
PERFORM OUT TABLES JTAB.
FORM FILL TABLES F_ITAB LIKE ITAB[].
DO 3 TIMES.
F_ITAB-COL1 =
SY-INDEX.
F_ITAB-COL2 = SY-INDEX ** 2.
APPEND F_ITAB.
ENDDO.
ENDFORM.
FORM OUT TABLES F_ITAB LIKE JTAB.
LOOP AT F_ITAB.
WRITE: /
F_ITAB-COL1, F_ITAB-COL2.
ENDLOOP.
ENDFORM.
The produces the following output:
1 1
2 4
3 9
In this example, an internal table ITAB is declared with a
header line and an internal table JTAB is declared without a header line. The
actual parameter ITAB is passed to the formal parameter F_ITAB of the subroutine
FILL in the TABLES addition. The header line is passed with it. After the body
of the table has been copied from ITAB to JTAB, the actual parameter is passed
to the formal parameter F_ITAB of the subroutine OUT using the TABLES addition.
The header line F_ITAB, which is not passed, is generated automatically in the
subroutine.
Passing Parameters to Subroutines Locate the document in its
SAP Library structure
If a subroutine has a parameter interface, you must supply
values to all of the formal parameters in its interface when you call it. You
list the actual parameters after the USING or CHANGING addition in the PERFORM
statement.
When you pass the values, the sequence of the actual
parameters in the PERFORM statement is crucial. The value of the first actual
parameter in the list is passed to the first formal parameter, the second to
the second, and so on. The additions USING and CHANGING have exactly the same
meaning. You only need to use one or the other. However, for documentary
reasons, it is a good idea to divide the parameters in the same way in which
they occur in the interface definition.
Actual parameters can be any data objects or field symbols
of the calling program whose technical attributes are compatible with the type
specified for the corresponding formal parameter. When you specify the actual
parameters, note that any that you pass by reference to a formal parameter, and
any that you pass by value to an output parameter, can be changed by the
subroutine. You should therefore ensure that only data objects that you want to
be changed appear in the corresponding position of the actual parameter list.
If a subroutine contains TABLES parameters in its interface,
you must specify them in a TABLES addition of the PERFORM statement before the
USING and CHANGING parameters. TABLES parameters are only supported to ensure
compatibility with earlier releases, and should no longer be used.
You can use offset addressing for actual parameters in the
same way as offset addressing for field symbols. That is, you can select memory
areas that lie outside the boundaries of the specified actual parameter.
Example
PROGRAM form_test.
DATA: a1 TYPE p DECIMALS 3,
a2 TYPE i,
a3 TYPE d,
a4 TYPE
spfli-carrid,
a5(1) TYPE c.
...
PERFORM subr USING a1 a2 a3 a4 a5.
...
PERFORM subr CHANGING a1 a2 a3 a4 a5.
...
PERFORM subr USING a1 a2 a3
CHANGING
a4 a5.
...
FORM subr USING
value(f1)
TYPE p
value(f2)
TYPE i
f3 LIKE a3
CHANGING
value(f4)
TYPE spfli-carrid
f5.
...
ENDFORM.
This example defines a subroutine SUBR with a parameter
interface consisting of five formal parameters, F1 to F5. The subroutine is
called internally three times. The actual parameters are the data objects A1 to
A5. The three subroutine calls are all equally valid. There are further PERFORM
statements that are also equally valid, so long as the sequence of the actual
parameters remains unchanged. In each call, A1 is passed to F1, A2 to F2, and
so on. When the subroutine ends, A3, A4, and A5 receive the values of F3, F4,
and F5 respectively. The third of the subroutine calls documents in the program
what the parameter interface of the subroutine shows, namely that only A4 and
A5 are changed. Whether A3 is changed depends on the way in which the
subroutine is programmed.
The following example shows how generically-typed formal
parameters inherit their technical attributes from their corresponding actual
parameters.
Example
REPORT demo_mod_tech_describe.
DATA:
date1 TYPE d, date2 TYPE t,
string1(6) TYPE
c, string2(8) TYPE c,
number1 TYPE p DECIMALS 2, number2
TYPE p DECIMALS 0,
count1 TYPE i, count2 TYPE i.
PERFORM typetest USING date1 string1 number1 count1.
SKIP.
PERFORM typetest USING date2 string2 number2 count2.
FORM typetest USING now
txt TYPE c
value(num) TYPE p
int TYPE i.
DATA: t(1) TYPE c.
DESCRIBE FIELD now
TYPE t.
WRITE: / 'Type of
NOW is', t.
DESCRIBE FIELD txt
LENGTH t IN CHARACTER MODE.
WRITE: / 'Length of
TXT is', t.
DESCRIBE FIELD num
DECIMALS t.
WRITE: / 'Decimals
of NUM are', t.
DESCRIBE FIELD int
TYPE t.
WRITE: / 'Type of
INT is', t.
ENDFORM.
This produces the following output:
Type of NOW is D
Length of TXT is 6
Decimals of NUM are 2
Type of INT is I
Type of NOW is T
Length of TXT is 8
Decimals of NUM are 0
Type of INT is I
An internal subroutine TYPETEST is called twice with
different actual parameters. All actual and formal parameters are compatible
and no error message occurs during the syntax check. Had you declared COUNT2
with type F instead of type I, the syntax check would have returned an error,
since the formal parameter INT is specified with type I. The formal parameters
with generic types adopt different technical attributes depending on their
corresponding technical attributes.