Problem solve Get help with specific problems with your technologies, process and projects.

The call of the prototype

Paul Tuohy explains the benefits of prototyping dynamic calls.

A lot of RPG programmers are under the misconception that the CALLP operation means Call Procedure. That is because...

most of them come across it when they start using subprocedures. But CALLP means Call a Prototyped Procedure or Program, and it can be used in place of the CALL operation, as well as CALLB.

I would like to discuss the benefits of prototyping dynamic calls. No mention of subprocedures or ILE.

What's wrong with CALL and PARM?
In RPG all parameters are passed by reference. That means a pointer to the parameter is passed, not the actual value of the parameter. That, in turn, means both the passed and receiving parameter fields share the same memory location, and that is where the potential problem lies.

More on this topic

Figure 1 shows the code of a calling program. It calls PGMB passing Parm1, a 10 character field, as a parameter.

D TestParm        DS
D  Parm1                          10  Inz('XXXXXXXXXX')
D  Parm2                          10  Inz('YYYYYYYYYY')

C                    Call       'PGMB'
C                    Parm                     Parm1

Figure 1: A program call with a parameter field in a data structure

Figure 2 shows the code of the called program. It has an incorrect length of 15 for Parm1.

Will the compiler tell us that it is invalid? No.

Will the program fail at run time? No.

What will happen? When control returns to the calling program, Parm1 will have a value of 'ZZZZZZZZZZ' and Parm2 will have a value of 'ZZZZZYYYYY'. Just imagine the fun if Parm2 was a packed numeric field.

D  Parm1          S               15

C     *Entry         PList
C                    Parm                     Parm1
C                    Eval        Parm1 = *All'Z'
C                    Return

Figure 2: The called program with an invalid length for a parameter.

You really want the compiler to validate the parameter list for you, which is one of the reasons for using prototypes.

The Prototype

When the compiler sees a CALLP operation, it will validate to ensure that all the parameters are correct. But how does it know that the parameters are correct? You provide a prototype. A prototype is the format, or the template, or the rules, for the call operation. It is not a parameter list.

Prototypes are defined on the D specifications, as shown in Figure 3. The format of a prototype is very similar to a data structure, except that the type is PR as opposed to DS. You can provide your own name for the CALLP (PromptProduct). The EXTPGM keyword indicates that this is the equivalent of a CALL operation, and it identifies the name of the called program (PRP01R). The names of the subfields in the prototype are irrelevant, what are important are the number of subfields (i.e. parameters) and the definition of each. In the example in Figure 3 the compiler will ensure that two parameters are passed, that Parm1 is a 30 character field and that Parm2 is a 1 character field.

D PromptProduct   PR                  ExtPgm('PRP001R')
D  FirstParm                    30
D  SecondParm                    1

C                   CallP     PromptProduct(Parm1:Parm2)

Figure 3: A prototyped call to a program.

I quite like the ability to use a meaningful name on the call.

The Procedure Interface

What about the called program? Just as the compiler will validate the parameters on the call, you also want it to validate the parameters in the called program. You achieve that by replacing the *ENTRY PLIST with a Procedure Interface, as shown in Figure 4. The compiler will, again, require a prototype to enable it to validate the parameters.

D PromptProduct   PR                  ExtPgm('PRP001R')
D  FirstParm                    30
D  SecondParm                    1

D PromptProduct   PI                  
D  GetCode                      30
D  Description                   1

Figure 4: A Procedure Interface.

Since the prototype is required in at least two programs, it makes sense to put it in a copy member and include it using the /COPY compiler directive.

And it makes even more sense to put all prototypes in a single copy member and include it in all programs using a /COPY compiler directive. Think of this as the source member containing the rules for all calls within your application.


The use of keywords in the prototype can ease some of your coding. The example in Figure 5 shows a work field (Parm2) being used to pass an action code (or some such).

C                    Eval       Parm2 = 'A'
C                    Call       'PGMB'
C                    Parm                     Parm1
C                    Parm                     Parm2

Figure 5: Using a work field on a parameter list

When using a prototyped call, you can use the CONST keyword to indicate that a constant value can be passed for the parameter, as shown in Figure 6.

D PromptProduct   PR                  ExtPgm('PRP001R')
D  FirstParm                    30
D  SecondParm                    1    Const

C                   CallP     PromptProduct(Parm1:'A')

Figure 6: Passing a constant value as a parameter.

Of course, you must make sure that the called program does not change the second parameter. If you are using a procedure interface, in the called program, the compiler will give you an error if you try to change a parameter that has a CONST keyword.

Another useful keyword is OPTION with a value of *OMIT or *NOPASS. Figure 8 shows an example of calling a program to set a customer name and address. The name is optional, as are the second and third address lines. OPTION(*OMIT) indicates that a value of *OMIT can be used in place of a parameter. OPTION(*NOPASS) indicates that the parameter is optional. Once a parameter is defined with OPTION(*NOPASS), all following parameters must be defined with OPTION(*NOPASS). All three calls are valid.

D SetCustomer     PR                  ExtPgm('SPR001R')                                        
D  Customer                     10  
D  Name                         30    Const Options(*OMIT)
D  AddressLine1                 30    Const                  
D  AddressLine2                 30    Options(*NOPASS)                    
D  AddressLine3                 30    Options(*NOPASS)                    
C                   CallP     SetCustomer(Code:CusName:
C                   CallP     SetCustomer(Code:*OMIT:Add1)
C                   CallP     SetCustomer(Code:'Paul':
C                                Add1:Add2)

Figure 7: A prototypes call using *OMIT and *NOPASS.

Figure 8 shows a snippet of the called program. The name and address lines are set to their "default" values. The %ADDR BIF is used to check whether or not *OMIT was specified for the name; the address will be null if *OMIT was specified. The %PARMS BIF is used to check the number of parameters passed.

D SetCustomer     PI                                                         
D  Customer                     10  
D  NameIn                       30    Const Options(*OMIT)
D  Addr1In                      30    Const                  
D  Addr2In                      30    Options(*NOPASS)                    
D  Addr3In                      30    Options(*NOPASS)                    
C                   Eval      CustomerName = 'No Name'
C                   Eval      AddrLine2 = Addr1In
C                   Eval      AddrLine2 = *All'*'
C                   Eval      AddrLine3 = *All'*'
C                   If        %Addr(NameIn) <> *Null
C                   Eval      CustomerName = NameIn
C                   EndIf 
C                   If        %Parms() > 3
C                   Eval      AddrLine2 = Addr2In
C                   EndIf 
C                   If        %Parms() > 4
C                   Eval      AddrLine3 = Addr3In
C                   EndIf

Figure 8: Checking for *OMIT and *NOPASS in a called program.


Remember, prototyped calls are not just for subprocedures. They are a means by which the compiler can validate parameters, and they allow you flexibility in how you pass parameters.

Also, if at a later stage, you decide to change any of these programs to subprocedures, all you need to do is change the EXTPGM keyword to an EXTPROC keyword and compile accordingly.

About the author: Paul Tuohy is CEO of ComCon, an iSeries consulting company. He is the author of Re-Engineering RPG Legacy Applications and is one of the quoted industry experts in the IBM Redbook "Who Knew You Could Do That With RPG IV?"


Dig Deeper on iSeries CL programming