After you have completed this chapter, you will be able to
In addition to defining variables by using tables, local, data, and statics, variables can also be defined on the form statement itself. These are known as parameters. Parameters can be either local or references to global variables. The memory for local parameters is allocated when the subroutine is called and freed when it ends.
If you define variables on the form statement, the perform statement must pass a value to each of these variables.
Parameter names that appear on the form statement are called formal parameters. This term is easy to remember because "formal" starts with "form." For example, in the statement form s1 using p1 changing p2 p3, the parameters p1, p2, and p3 are called formal parameters.
Parameter names that appear on the perform statement are called actual parameters. For example, in the statement perform s1 using f1 changing f2 f3, the parameters f1, f2, and f3 are called actual parameters.
Defining variables as parameters is illustrated in Listing 18.1.
Listing 18.1 Passing Parameters to a Subroutine
1 report ztx1801. 2 data: f1 value 'A', 3 f2 value 'B', 4 f3 value 'C'. 5 6 perform: s1 using f1 f2 f3, 7 s2 using f1 f2 f3. 8 9 form s1 using p1 p2 p3. 10 write: / f1, f2, f3, 11 / p1, p2, p3. 12 endform. 13 14 form s2 using f1 f2 f3. 15 skip. 16 write: / f1, f2, f3. 17 endform.
The code in Listing 18.1 produces the following output:
A B C A B C A B C
Please review the syntax for the form and perform statements before continuing. See the section "Defining and Calling Internal Subroutines" in Chapter 19.
|DO used typed formal parameters-they are more efficient than untyped parameters.||DON'T pass internal tables by value or by value and result unless you really need a new copy of the internal table. Pass para-meters by reference whenever possible.|
Formal parameters can be either typed or untyped. A typed parameter is a formal parameter that has a data type following its name on the form statement. An untyped parameter is a formal parameter that doesn't have a data type following its definition on the form statement. In Listing 18.1, all parameters were untyped.
Untyped formal parameters allow you to pass a variable of any data type or length to it. The formal parameter uses the attributes of the actual parameter. For example, if you pass a four-byte integer to an untyped formal parameter p1, p1 becomes a four-byte integer. If you pass a character string length 3 to the same parameter, it will become character 3.
form s1 using u1 type t value(u2) type t changing c1 type t value(c2) type t.
The following points apply:
Passing a variable of the wrong data type or length to a typed parameter causes a syntax error. Listing 18.2 shows how to use typed parameters.
Listing 18.2 Using Typed Parameters
1 report ztx1802. 2 data: f1 value 'A', 3 f2 type i value 4, 4 f3 like sy-datum, 5 f4 like sy-uzeit. 6 7 f3 = sy-datum. 8 f4 = sy-uzeit. 9 10 perform s1 using f1 f2 f3 f4. 11 12 form s1 using p1 type c 13 p2 type i 14 p3 type d 15 p4 type t. 16 write: / p1, 17 / p2, 18 / p3, 19 / p4. 20 endform.
The code in Listing 18.2 produces the following output:
AAA 4 19980510 164836
Typed parameters have three advantages:
You can pass a field string the same way as any other parameter. However, if you want to access the components of the field string within the subroutine, you must make the structure of the field string known to the subroutine via one of two additions to the form statement:
Here, x can be a field string or a DDIC structure or table. For example, form s1 using fs1 structure ztxlfa1 defines fs1 as having the structure of DDIC table ztxlfa1.
There are three ways of passing parameters to a subroutine:
The syntax on the form statement determines how variables are passed. The syntax on perform does not. What this means will be explained in the next section. The first thing needed is to learn how to code each method.
Table 18.2 shows the relationship between syntax and passing methods.
|using v1||Pass by reference|
|changing v1||Pass by reference|
|using value(v1)||Pass by value|
|changing value(v1)||Pass by value and result|
Although the syntax on form and perform can differ, for the sake of program clarity they should be the same.
Listing 18.3 illustrates how to code these additions.
Listing 18.3 How to Code Parameter Additions
1 report ztx1803. 2 data: f1 value 'A', 3 f2 value 'B', 4 f3 value 'C', 5 f4 value 'D', 6 f5 value 'E', 7 f6 value 'F'. 8 9 perform s1 using f1 f2 10 changing f3 f4. 11 12 perform s2 using f1 f2 f3 f4 13 changing f5 f6. 14 15 perform s3 using f1 f2 f3. 16 17 form s1 using p1 value(p2) 18 changing p3 value(p4). 19 write: / p1, p2, p3, p4. 20 endform. 21 22 form s2 using p1 value(p2) value(p3) p4 23 changing value(p5) p6. 24 write: / p1, p2, p3, p4, p5, p6. 25 endform. 26 27 form s3 using value(p1) 28 changing p2 value(p3). 29 write: / p1, p2, p3. 30 endform.
The code in Listing 18.3 produces the following output:
A B C D A B C D E F A B C
Remember to keep the following things in mind:
Table 18.3 briefly describes the three methods of passing parameters.
|By reference||Passes a pointer to the original memory location.||Very efficient|
|By value||Allocates a new memory location for use within the subroutine. The memory is freed when the subroutine ends.||Prevents changes to passed variable|
|By value and result||Similar to pass by value, but the contents of the new memory is copied back into the original memory before returning.||Allows changes and allows a rollback|
When you pass a parameter by reference, new memory is not allocated for the value. Instead, a pointer to the original memory location is passed. All references to the parameter are references to the original memory location. Changes to the variable within the subroutine update the original memory location immediately. Figure 18.1 and Listing 18.4 illustrate how this works.
Figure 18.1 : How a parameter passed by reference affects the original memory location.
Listing 18.4 Effect of Pass by Reference
1 report ztx1804. 2 data f1 value 'A'. 3 4 perform s1 using f1. 5 write / f1. 6 7 form s1 using p1. 8 p1 = 'X'. 9 endform.
The code in Listing 18.4 produces the following output:
With internal subroutines, there is little difference between passing parameters by reference and accessing global variables from within the subroutine. Both allow you to change the value of a global variable directly. In external subroutines and function modules (see Chapter 19, "Modularization: Function Modules, Part 1") the pass by reference is more useful. Even so, passing parameters to a subroutine-be it internal or external-is good programming style. It makes maintenance easier and improves the readability of your program.
The additions using f1 and changing f1 both pass f1 by reference-they are identical in function. The reason they both exist is that-used properly-they can document whether the subroutine will change a parameter or not.
Code changing with parameters, the subroutine changes. You should code using with parameters that are not changed by the subroutine. Listing 18.5 illustrates this point.
Listing 18.5 using and changing Are Identical in
1 report ztx1805. 2 data: f1 value 'A', 3 f2 value 'B'. 4 5 write: / f1, f2. 6 perform s1 using f1 7 changing f2. 8 write: / f1, f2. 9 10 form s1 using p1 11 changing p2. 12 p1 = p2 = 'X'. 13 endform.
The code in Listing 18.5 produces the following output:
A B X X
f1 and f2 are both passed by reference to s1. Therefore, p1 and p2 are pointers to f1 and f2. Changes to p1 and p2 are immediately reflected in f1 and f2. This example only illustrates that it is possible to change any parameter that is passed by reference. You should code your parameters so that using and changing properly document your usage of those parameters.
When you pass a parameter by value, new memory is allocated for the value. This memory is allocated when the subroutine is called and is freed when the subroutine returns. Therefore, references to the parameter are thus references to a unique memory area that is known only within the subroutine; the original memory location is separate. The original is unchanged if you change the value of the parameter. Figure 18.2 and Listing 18.6 illustrate how this works.
Figure 18.2 : How a parameter passed by value allocates a new storage location independent of the original.
Listing 18.6 Effect of Pass by Value
1 report ztx1806. 2 data: f1 value 'A'. 3 4 perform s1 using f1. 5 write / f1. 6 7 form s1 using value(p1). 8 p1 = 'X'. 9 write / p1. 10 endform.
The code in Listing 18.6 produces the following output:
Use pass by value when you need a local copy of a variable that you can change without affecting the original. Pass by reference is more efficient than pass by value. Use pass by reference unless you need an independent local copy of the variable.
Pass by value and result is very similar to pass by value. Like pass by value, a new memory area is allocated and it holds an independent copy of the variable. It is freed when the subroutine ends, and that is also when the difference occurs.
When the endform statement executes, it copies the value of the local memory area back into the original memory area. Changes to the parameter within the subroutine are reflected in the original, but not until the subroutine returns.
This may seem like a small difference, but the difference become greater. You can change whether the copy takes place or not.
The copy always takes place unless you leave the subroutine by using one of two statements:
The stop statement terminates the subroutine and goes directly to the end-of-selection event. If p1 was passed by value and result, changes to p1 are discarded before end-of-selection is triggered. In a sense, stop behaves like a mini-rollback for value and result parameters. When it is used inside of a subroutine, the stop statement is usually preceded by a test for an abnormal condition within the program. If the abnormal condition arises, stop is executed. It discards the changes to value and result variables, and triggers end-of-selection, where cleanup procedures are then executed.
Use pass by value and result for parameters you want to change, but there may be a possibility that you will want to discard the changes if an abnormal condition should arise in your subroutine.
The message statement and its affect on subroutines is covered in Chapter 21, "Selection Screens."
Figure 18.3 and Listing 18.7 illustrate parameters passed by value and result.
Figure 18.3 : How a parameter passed by value and result allocates a new storage location independent of the original and copies the value back in again.
Listing 18.7 Effect of Pass by Value and Result
1 report ztx1807. 2 data: f1 value 'A'. 3 4 perform: s1 changing f1, 5 s2 changing f1. 6 7 end-of-selection. 8 write: / 'Stopped. f1 =', f1. 9 10 form s1 changing value(p1). 11 p1 = 'B'. 12 endform. 13 14 form s2 changing value(p1). 15 p1 = 'X'. 16 stop. 17 endform.
The code in Listing 18.7 produces the following output:
Stopped. f1 = B
It may be a good idea to review internal tables now, especially the it syntax.
You can use one of two methods to pass an internal table to a subroutine:
If the internal table has a header line, method 1 passes both the header line and the body to the subroutine. Method 2 passes only the body to the subroutine.
If the internal table doesn't have a header line, you can also use both methods. However, method 1 will behave a little differently-it will automatically create a header line for the internal table within the subroutine.
At this point, you may be asking yourself, "Why would I want to pass an internal table without a header line?" Most of the time, you wouldn't. However, if you have a special case that requires an internal table without a header line, you will need to pass it without a header line. You will need to do this if you use nested internal tables-a nested internal table cannot have a header line. Nested internal tables are used more often in release 4, so this technique will be needed more often on newer systems.
Table 18.4 summarizes the effect of each of these methods on internal
tables with and without header lines.
Has a Header Line
Have a Header Line
|With header line||Passes both||Creates a header line inside subroutine|
|Without header line||Passes body only||Passes body|
Table 18.5 shows the syntax for each method of passing an internal
table to a subroutine.
|With header line||form s1 tables it||By reference|
|Body only||form s1 using it
form s1 changing it
form s1 using value(it)
form s1 changing value(it)
By value and result
If the internal table has a header line, the first method in Table 18.4 passes both the header and body to the subroutine. The rest of the methods pass the body only.
If the internal table doesn't have a header line, the first method in Table 18.4 passes the body and creates a header line within the subroutine. The rest pass the body only.
Merely passing an internal table is usually not enough-you must also describe the structure of the internal table to the subroutine. If you don't describe the structure of the internal table on the form statement, the components of the internal table will be unknown within the subroutine. Accessing any component within the subroutine will then result in a syntax error.
The syntax you need depends on the method you use to pass the
internal table. Table 18.6 shows the appropriate syntax for each
|With header line||tables it structure x||A field string
A DDIC structure
A DDIC table
An internal table with a header line
|tables it like x||An internal table without aheader line
An internal table body (it)
|Without header line||using it like x||An internal table without header line
An internal table body (it)
The structure addition expects a field string name after it. Here, code the name of any field string, DDIC structure or table, or the name of an internal table that has a header line.
The like addition expects a table body after it. Here, code a reference to the body of an internal table. If the internal table you wish to name here doesn't have a header line, either it or it refers to the body of it. If it has a header line, only the syntax it can be used to refer to the body of it.
If you pass the body only, you will usually need a work area within the subroutine to add records to and retrieve records from the internal table. To define it, you can use local, data, or statics. If a global work area is already available, you might use that one, although it is less desirable because accessing global variables from within a subroutine makes program maintenance more difficult. If you elect to use the data statement to define your work area, the like line of itabbody addition is available. It defines a field string using only the body of an internal table. The resulting field string is exactly like a line of the internal table body itabbody. For example, data fs like line of it defines a field string named fs. It has the same structure as a single row of it.
If an internal table has a header line and you want to pass both the header line and the body to a subroutine, use the syntax shown in the first row of Table 18.5. This passes both the header line and the body, and they are both passed by reference. Therefore, changes made to either the header line or body of the internal table within the subroutine are immediately reflected in the original.
Listing 18.8 illustrates this syntax.
Listing 18.8 How to Pass an Internal Table That
Has a Header Line with Its Header Line
1 report ztx1808. 2 * Here, IT is an internal table that has a header line 3 * This program shows how to pass both the header line and body. 4 tables ztxlfa1. 5 data it like ztxlfa1 occurs 5 with header line. 6 7 select * up to 5 rows from ztxlfa1 into table it order by lifnr. 8 perform s1 tables it. 9 loop at it. 10 write / it-lifnr. 11 endloop. 12 13 form s1 tables pt structure ztxlfa1. "uses the field string ztxlfa1 14 "here, you can use: 15 "structure fs "a field string 16 "structure ddicst "a ddic structure or table 17 "structure it "any internal table with header line 18 "like it. "ok if itab doesn't have header line 19 "like it. "any internal table 20 read table pt index 3. 21 pt-lifnr = 'XXX'. 22 modify pt index 3. 23 endform.
The code in Listing 18.8 produces the following output:
1000 1010 XXX 1030 1040
If an internal table doesn't have a header line and you want to pass the body and create a header line in the subroutine, you can also use the syntax shown in the first row of Table 18.5. This passes the body by reference, and creates a header line locally in the subroutine. Changes made to the body of the internal table within the subroutine are immediately reflected in the original.
Listing 18.9 illustrates this syntax.
Listing 18.9 How to Pass an Internal Table Without
a Header Line to a Subroutine and Automatically Create a Header
1 report ztx1809. 2 * Here, an internal table that doesn't have a header line 3 * is passed with header line 4 tables ztxlfa1. 5 data: it like ztxlfa1 occurs 5. "doesn't have a header line 6 7 select * up to 5 rows from ztxlfa1 into table it order by lifnr. 8 perform s1 tables it. 9 loop at it into ztxlfa1. "need to use a work area because it 10 write / ztxlfa1-lifnr. "doesn't have a header line 11 endloop. 12 13 form s1 tables pt structure ztxlfa1. "or you can use: 14 " like it 15 " like it 16 read table pt index 3. 17 pt-lifnr = 'XXX'. 18 modify pt index 3. 19 endform.
The code in Listing 18.9 produces the following output:
1000 1010 XXX 1030 1040
If an internal table doesn't have a header line and you want to pass the body without automatically creating a header line in the subroutine, you can use the syntax shown in rows two through five of Table 18.5. By using this syntax, you can pass the body by reference, by value, or by value and result. If you pass it by reference, changes made to the body of the internal table within the subroutine are immediately reflected in the original. If you pass it by value, a local copy of it is created-changes are discarded at the end of the subroutine when the local memory for it is freed. If you pass it by value and result, changes are copied back into the original when endform is executed. A stop statement within the subroutine will discard all changes to it and transfer control directly to end-of-selection.
Listing 18.10 illustrates these methods.
Listing 18.10 How to Pass an Internal Table Without
a Header Line to a Subroutine
1 report ztx1810. 2 * Here, an internal table that doesn't have a header line 3 * is passed without creating a header line automatically 4 tables ztxlfa1. 5 data: it like ztxlfa1 occurs 5. "doesn't have a header line 6 7 select * up to 5 rows from ztxlfa1 into table it order by lifnr. 8 perform: s1 using it, 9 s2 using it, 10 s3 using it, 11 writeitout tables it. 12 13 end-of-selection. 14 write: / 'In End-Of-Selection'. 15 perform writeitout tables it. 16 17 form s1 using value(pt) like it. "pass by value 18 * you can also use: like it. 19 data wa like line of pt. 20 * you can also use: 21 * data wa like ztxlfa1. 22 * data wa like line of pt. 23 * local ztxlfa1. "if you use this, the work area name 24 * "will be ztxlfa1, not wa 25 read table pt into wa index 1. 26 wa-lifnr = 'XXX'. 27 modify pt from wa index 1. 28 endform. 29 30 form s2 using pt like it. "pass by reference 31 data wa like line of pt. 32 read table pt into wa index 2. 33 wa-lifnr = 'YYY'. 34 modify pt from wa index 2. 35 endform. 36 37 form s3 changing value(pt) like it. "pass by value and result 38 data wa like line of pt. 39 read table pt into wa index 3. 40 wa-lifnr = 'ZZZ'. 41 modify pt from wa index 3. 42 stop. 43 endform. 44 45 form writeitout tables it structure ztxlfa1. 46 loop at it. 47 write / it-lifnr. 48 endloop. 49 endform.
The code in Listing 18.10 produces the following output:
In End-Of-Selection 1000 YYY 1020 1030 1040
An external subroutine is one that resides in a different program than the perform statement that calls it. Figure 18.4 illustrates an external subroutine.
Figure 18.4 : An illustration of an external subroutine.
When a perform calls an external subroutine
The fact that the syntax check occurs at runtime is important, for the following two reasons:
External subroutines are very similar to internal subroutines:
Both allow local variable definitions.
Figure 18.5 illustrates the differences between internal and external subroutines.
Figure 18.5 : The differences between internal and external subroutines.
The following are differences between external and internal subroutines:
Listings 18.11 and 18.12 illustrate a call to an external subroutine.
Listing 18.11 Calling the Subroutine Shown in Listing
1 report ztx1811. 2 tables ztxlfa1. 3 data f1(3) value 'AAA'. 4 5 ztxlfa1-lifnr = '1000'. 6 perform s1(ztx1812). 7 write: / 'f1 =', f1, 8 / 'lifnr =', ztxlfa1-lifnr.
Listing 18.12 Subroutine Called from the Program
in Listing 18.11
1 report ztx1812. 2 tables ztxlfa1. 3 data f1(3). 4 5 form s1. 6 f1 = 'ZZZ'. 7 ztxlfa1-lifnr = '9999'. 8 endform.
The code in Listing 18.11 produces the following output:
f1 = AAA lifnr = 9999
Use the local statement within external subroutines to avoid sharing tables work areas.
|Why do I need to know all of the variations of passing an internal table with header line and without? Don't just a few of these variations cover all circumstances?|
|If you were merely creating programs in ABAP/4, you wouldn't need to know all of these variations. You could code most of your programs with just a few simple variations. However, the majority of an ABAP/4 programmer's time is not usually spent creating ABAP/4 code-it is spent reading it. Passing internal tables to subroutines is a sticky subject, and 80 percent of your time will be spent reading code that does these sticky things-less than 20 percent of your time will be spent creating your own code. This sad imbalance dictates that you must learn the variations used by SAP, so you can spend less time pulling your hair out while trying to understand the SAP code. This will leave you with more time for writing your own code, and more hair.|
The Workshop provides two ways for you to affirm what you've learned in this chapter. The Quiz section poses questions to help you solidify your understanding of the material covered and the Exercise section provides you with experience in using what you have learned. You can find answers to the quiz questions and exercises in Appendix B, "Answers to Quiz Questions and Exercises."
Run the program shown in Listing 18.13. Comment out the parameters statement and run it again. Describe the sequence of events that occur in each case.
Listing 18.13 Explain the Sequence of Events That
Occurs in this Program
report ztx1813 no standard page heading. ztx1813 data: flag, ctr type i. parameters p1. initialization. flag = 'I'. write: / 'in Initialization'. start-of-selection. flag = 'S'. write: / 'in Start-Of-Selection', / 'p1 =', p1. top-of-page. add 1 to ctr. write: / 'Top of page, flag =', flag, 'ctr =', ctr. uline.