If your development environment consists primarily of CL and RPG, you may want to look at how you prototype the RPG subprocedures in your service programs. Why, you may wonder?
The short answer is so that you don't have problems with data integrity when you use CallPrc (Call procedure) in CL to invoke subprocedures. The data integrity problem occurs when you use return values or parameters passed by value for data types that CL handles differently than RPG (for example, one-byte character or indicator types).
Consider the following sample service program:
// ==============================================================
// = Sample service program =
// ==============================================================
H NoMain
D MyProc Pr N
D ParameterIn 10
P MyProc B Export
D MyProc PI N
D InputFld 10
/Free
If InputFld = *Blank ;
Return *Off ;
Else ;
Return *On ;
EndIf ;
/End-Free
P MyProc E
The above service program contains subprocedure MyProc, which accepts a single parameter as input and returns an indicator field. When the input parameter is blank, MyProc returns the indicator in the *Off state. Likewise, when the input parameter is not blank, MyProc returns the indicator in the *On state.
Let's look at an RPG module that uses subprocedure MyProc.
// ==============================================================
// = RPG module bound to service program =
// ==============================================================
D MyProc Pr N
D ParameterIn 10
D RtnFlag S N
D SomeFld S 10
/Free
SomeFld = *Blank ;
RtnFlag = MyProc( SomeFld ) ;
SomeFld = 'SomeValue' ;
RtnFlag = MyProc( SomeFld ) ;
*InLR = *On ;
/End-Free
After the first invocation of MyProc, field RtnFlag is in the *Off state because SomeFld is blank. The second invocation of MyProc results in the *On state for RtnFlag.
Now, let's look at a CL module that uses subprocedure MyProc.
/* =============================================================== */
/* = CL module bound to sample service program = */
/* =============================================================== */
Pgm
Dcl &RtnFlag *Lgl
Dcl &SomeFld *Char ( 10 )
ChgVar &SomeFld ( ' ' )
CallPrc Prc( MyProc ) +
Parm( &SomeFld ) +
RtnVal( &RtnFlag )
If ( &RtnFlag ) +
Do
.
.
.
EndDo
ChgVar &SomeFld ( 'SomeValue' )
CallPrc Prc( MyProc ) +
Parm( &SomeFld ) +
RtnVal( &RtnFlag )
If ( &RtnFlag ) +
Do
.
.
.
EndDo
EndPgm
When you execute this CL, the first invocation of subprocedure MyProc appears to run correctly. That is, the first Do block executes as you expect. However, you may be surprised when the Do block following the second invocation of MyProc fails to execute.
Using a single-byte field (as is &RtnFlag) results in garbage in the return value because CL and RPG pass the return value differently. One workaround is to define a two-byte field for the return variable and use substring functions to extract the correct value from the second byte. A kludge at best, this technique doesn't appeal to me.
A more acceptable solution is the use of RPG keyword ExtProc to instruct RPG to pass the troublesome values in the manner used by CL. The change is simple. You just add to the prototype the keyword with the *CL argument along with the subprocedure name. For instance, the following prototype replaces the one from the above examples:
D MyProc Pr N ExtProc( *CL : 'MYPROC' )
Once you use this prototype in the service program, the CL program will function correctly. You must be sure to use this same prototype in the RPG programs that invoke subprocedure MyProc (i.e., the calling programs) so that both caller and called programs agree.