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
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:
Recall that our procedure interface looked like this:
We are going to code our procedure prototype in the mainline source like this:
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:
Now, lets use the procedure to test the first date:
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:
We can also use the not operator to test for the converse of an expression, as in:
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:
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:
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.
In addition to calling a procedure within an expression, you can also use the CALLP (call procedure) operation code:
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:
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.
Send mail to email@example.com
with questions or comments about this web site.
Last Modified: September 06, 2000