Now let us look at an example of a multiple procedures module. In this 'mini-application' we are writing a program ARRSRPT to produce a report of all customers whose accounts are in arrears. We will create the basic report as a module, so that it can be bound to other modules, if necessary. There are two main tasks that are required for this module:
We have decided to code each task as a subprocedure. Conceptually, the module will look something like that shown in Figure 17.
Now consider the first subprocedure, InArrears, which is shown in Figure 18. InArrears is called by the main procedure to determine if the current record is in arrears.
When coding subprocedures that use global fields, you may want to establish a naming convention that shows the item to be global. In this example, the uppercase field names indicate DDS fields. Another option would be to prefix 'g_', or some other string to indicate global scope.
If the record is in arrears, the subprocedure returns '1' to the main procedure.
//--------------------------------------------------------------
// InArrears
//
// Parameters: (none)
// Globals: DUEDATE, AMOUNT, CurDate
//
// Returns: '1' if the customer is in arrears
//--------------------------------------------------------------
P InArrears B 1
D InArrears PI 1A 2
// Local declarations
D DaysLate S 10I 0 3
D DateDue S D 3
// Body of procedure
/free
DateDue = %date (DUEDATE: *ISO);
DaysLate = %diff (CurDate: DateDue: *d);
// The data in the input file comes from another type
// of computer, and the AMOUNTC field is a character
// string containing the numeric value. This string
// must be converted to the numeric AMOUNT field
// for printing.
AMOUNT = %dec(AMOUNTC : 31 : 9);
if DaysLate > 60 AND AMOUNT > 100.00;
return '1'; 4
endif;
return '0'; 4 5
/end-free
P InArrears E 1
Figure 18 shows the main elements that are common to all subprocedures.
For all subprocedures, and also for a cycle-main procedure with prototyped entry parameters, you need to define a procedure interface. A procedure interface definition is a repeat of the prototype information, if the prototype was specified, within the definition of a procedure. It is used to define the entry parameters for the procedure. The procedure interface definition is also used to ensure that the internal definition of the procedure is consistent with the external definition (the prototype). When the prototype is not specified, the compiler generates the prototype from the procedure interface, so the procedure interface definition provides both the internal definition and the external definition. In the case of InArrears, there are no entry parameters.
Consider next the subprocedure FmtCust, which is shown in Figure 19. FmtCust is called by ARRSRPT to format the relevant fields of a record into an output record for the final report. (The record represents an account that is in arrears.) FmtCust uses global data, and so does not have any input parameters. It formats the data into two output fields: one for the name, and one for the address.
//--------------------------------------------------------------
// FmtCust formats CUSTNAME, CUSTNUM, STREETNAME etc into
// readable forms
//
// Parameters: Name (output)
// Address (output)
// Globals: CUSTNAME, CUSTNUM, STREETNUM, STREETNAME, CITY
// STATE, ZIP
//--------------------------------------------------------------
P FmtCust B
D FmtCust PI
D Name 100A
D Address 100A
/free
//--------------------------------------------------------------
// CUSTNAME and CUSTNUM are formatted to look like this:
// A&P Electronics (Customer number 157)
//--------------------------------------------------------------
Name = CUSTNAME + ' ' + '(Customer number '
+ %char(CUSTNUM) + ')';
//--------------------------------------------------------------
// Call the FmtAddr procedure to handle the address
//--------------------------------------------------------------
Address = FmtAddress (STREETNUM : STREETNAME :
CITY : STATE : ZIP);
/end-free
P FmtCust E
Finally, consider the last subprocedure of this application, FmtAddr. Notice that FmtAddr does not appear in the ARRSRPT module, that is shown in Figure 17. We decided to place FmtAddr inside another module called FMTPROCS. FMTPROCS is a utility module that will contain any conversion procedures that other modules might need to use.
Figure 20 shows the source of the module FMTPROCS. Since procedure FmtAddr is called from another module, a prototype is required. So that the prototype can be shared, we have placed the prototype into a /COPY file which is copied into both the calling module, to provide information about how to call the procedure, and into the module that defines the procedure, to ensure that the prototype matches the procedure interface.
//=================================================================
// Source for module FMTPROCS. This module does not have a
// main procedure, as indicated by the keyword NOMAIN.
//=================================================================
H NOMAIN
//-----------------------------------------------------------------
// The prototype must be available to EACH module containing
// a prototyped procedure. The /COPY pulls in the prototype
// for FmtAddr.
//-----------------------------------------------------------------
D/COPY QRPGLESRC,FMTPROC_P
P FmtAddr B EXPORT
D FmtAddr PI 100A VARYING
D streetNum 10I 0 CONST
D streetName 50A CONST
D city 20A CONST
D state 15A CONST
D zip 5P 0 CONST
/free
//--------------------------------------------------------------
// STREETNUM, STREETNAME, CITY, STATE, and ZIP are formatted to
// look like:
// 27 Garbanzo Avenue, Smallville IN 51423
//--------------------------------------------------------------
return %char(streetNum) + ' ' + %trimr(streetName)
+ ', ' + %trim(city) + ' ' + %trim(state)
+ ' ' + %editc(zip : 'X');
P FmtAddr E
FMTPROCS is a NOMAIN module, meaning that it consists only of subprocedures; there is no main procedure. A NOMAIN module compiles faster and requires less storage because there is no cycle code that is created for the module. You specify a NOMAIN module, by coding the NOMAIN keyword on the control specification. For more information on NOMAIN modules, see Program Creation.