Midware Ltd.

Calling a Procedure

Home
Services
News
AS/400
Employment

Sign-up for e-mail notifications

Take our weekly poll

Dow Jones Intraday

Nasdaq Intraday

 

 

Now that we know how to code an internal procedure, we need to know how to call it.  Before we can call it we need to do a little bit of prep work.


The Procedure Prototype

Note:  The procedure prototype is probably the most confusing aspect of procedures - not because it's difficult to code, but because it's difficult to see why it's necessary at all.  When we cover external procecures and service programs, it will begin to make more sense.  For now, just trust me that it really is needed.

Remember when we coded the procedure itself we had to code the procedure interface - which is similar to an entry parm list - in the D-specs?  Well, in the mainline source we also need the counterpart to the procedure interface - the procedure prototype.

The prototype looks very similar to the procedure interface with a couple of exceptions:

  1. "PR" is coded in positions 24-25 instead of "PI".
  2. The field names of the procedure's parameters do not need to match what was defined on the procedure interface.  In fact, you don't need to code field names at all.
  3. As always, capitalization and indentation does not need to match.

Recall that our procedure interface looked like this:

*.. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 .
DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++
D IsWorkday       PI              N
D  @Date                          D   datfmt(*iso)

We are going to code our procedure prototype in the mainline source like this:

D IsWorkday       PR              N
D                                 D   datfmt(*iso)

We could have (in fact it's often a good idea) coded field names for the procedure's parameters.  Field names can be used as a form of documentation for the procedure.  In this case, it's pretty obvious what the procedure is doing from it's name, and we know from the "D" in position 40 that it accepts a date parameter by the type.

Aside from the three allowable differences noted above, the prototype must look exactly like the interface.  If there are numeric or alpha-numeric parameters, the lengths and type must match.

The procedure prototype does three things.  First, it tells the compiler that we may (we are not required to) call the procedure somewhere in the program.  Next, it establishes what parameters are required by the procedure and what the return value will be.  Also, it is used to check that the calling program is passing the correct parameter types to the procedure.  If the prototype does not match the interface, the program will not compile.


Calling The Procedure

Now that we have the procedure prototype coded, we are free to use the procedure.  There are actually several ways to call the procedure.  The most natural way is to code it in a free-form expression similar to built-in-functions.  A procedure may be used anyplace a built-in-function can be used.  In fact, procedures really are your own versions of built-in-functions (they are user-written however rather than built in).  Another way of looking at this is that built-in-functions are really just IBM-written procedures.

Lets first set up a couple of stand-alone date fields to test the procedure:

D @TestDate1      S               D   inz(d'2000-04-29')
D @TestDate2      S               D   inz(d'2000-04-28')
D @TestDate3      S               D   inz(d'2000-01-02')

Now, lets use the procedure to test the first date:

C                   if        IsWorkday(@TestDate1)
C***  do something
C                   else
C***  do something else
C                   endif

The procedure IsWorkday will be called, passing @TestDate1 as the parameter.  If multiple parameters would have been required, they would be separated with a colon (:).  The procedure will run and the return value will be used in place of the procedure name in the expression.  Because @TestDate1 is a Saturday, a value of *off (false) will be returned and the else code will be executed.  We are able to code the if statement like this because the procedure is returning an indicator type value.  The following expression is identical, but not quite as readable:

C                   if        IsWorkday(@TestDate1) = *on

We can also use the not operator to test for the converse of an expression, as in:

C                   if        not IsWorkday(@TestDate1)

Any operation code that allows a free-form expression may be used with a procedure.  This includes, DOU, DOW, WHEN (used with SELECT), and EVAL.  The EVAL operation may be used to assign a value to a variable based on the result of a procedure call:

C                   eval       *in99 =
C                                 IsWorkday(@TestDate2)

Because @TestDate2 is a Friday, *IN99 will be set to *on.

Keep in mind also that both sides of a free-format expression must have the same data type.  This implies that the compiler thinks of the IsWorkday procedure as an indicator.  This brings up an interesting way of looking at procedures.  Procedure can actually be thought of as a smart variable.  The data type of the variable is defined on the procedure prototype D-spec.  Look at the following three examples:

DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++
D @Nbr            S              3p 0 inz(123)
D @Str            C              1a   const('X')
D @Proc           PR            10a

In the first line, we've defined the name @Nbr as a stand alone field 3-digits long, @Str as a three-byte alphanumeric constant, and @Proc as a ten-byte alphanumeric procedure.  In this sense, a procedure is very close in concept to just another data type.  Like a constant, it's value cannot be set, only retrieved, but unlike a constant, it's value is dynamic - depending on parameters or the system environment.


Callp

In addition to calling a procedure within an expression, you can also use the CALLP (call procedure) operation code:

C                   callp      IsWorkday(@TestDate3)

Again, if multiple parameters are required, they are separated with a colon (:).  The main difference between CALLP and using the procedure in an expression is the lack of a return value.  If a procedure does not return a value, CALLP (or CALLB - call bound) must be used.  If a procedure does return a value, it will be ignored using the CALLP operation.

With our IsWorkday procedure, it doesn't make much sense to use the CALLP operation.  The benefit of calling the procedure is for the return value.  Procedure can do other tasks, however (such as updating a file) that may be done without returning a value.

Procedures may also return values implicitly by changing the values of the parameters passed into it.  In fact, there is a whole library of pre-written C procedures that do this.  With the ability to call a procedure now, RPG has access to all this functionality.  We'll cover more on this later.


Putting it all Together

Out complete program now looks like this:

H datedit(*ymd) option(*srcstmt)
 *
 * Define the prototype for the procedure...
D IsWorkday       PR              N
D                                 D   datfmt(*iso)
 *
 * Define some stand-alone test fields...
D @TestDate1      S               D   inz(d'2000-04-29')
D @TestDate2      S               D   inz(d'2000-04-28')
D @TestDate3      S               D   inz(d'2000-01-02')

 *
 * Calling the procedure inside an IF expression...
C                   if        IsWorkday(@TestDate1)
C***  do something
C                   else
C***  do something else
C                   endif

 *
 * Calling the procedure inside an EVAL expression...
C                   eval       *in99 =
C                                 IsWorkday(@TestDate2)

 *
 * Calling the procedure with CALLP...
C                   callp      IsWorkday(@TestDate3)

 *
 * End the program...
C                   eval       *inlr = *on
 *
 *======================================================*
 * IsWorkday:  Determine if a given date is a normal 
 *             workday.
 *======================================================*
P IsWorkday       B

D IsWorkday       PI              N
D  @Date                          D   datfmt(*iso)

 * Define @BaseDate as a Sunday
D @BaseDate       S               D   inz(d'2000-01-02')

D @Days           S              9  0
D @DayOfWeek      S              1  0
D @WorkDay        S               N

 *
 * How many days are there between our base date and
 * the passed date?
C     @BaseDate     subdur    @Date         @Days:*d
 *
 * Based on the number of days, figure out the day of
 * week of the passed date (0=Sunday, 1=Monday, etc.)
C                   eval      @DayOfWeek = %rem(@Days: 7)
 *
 * If date falls on a weekend, it is not a workday...
C                   if        @DayOfWeek = 0 or
C                             @DayOfWeek = 6
C                   eval      @WorkDay = *off
C*
 * Otherwise, it is...
C                   else
C                   eval      @WorkDay = *on
C                   endif
 *
C                   return    @WorkDay

P IsWorkday       E


Compiling the program

RPG IV programs with internal procedures may also be created with the one-step CRTBNDRPG command.  Remember however, that behind the scenes a module is being created, and then the program is created from the module.

The one problem you may run into when compiling a program with an internal procedure, is the activation group parameter.  Activation groups are a somewhat more advanced ILE concept that we'll cover later.  For now, specificy DFTACTGRP(*NO) and ACTGRP(QILE) and your program should compile fine.

  Back to Coding an Internal Procedure Next to Typecasting
 
Home Feedback Contents Search

Send mail to midware@midwareservices.com with questions or comments about this web site.
Copyright 2000 Midware, Ltd.

Last Modified:  September 06, 2000