Example of Module with Multiple Procedures

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:

  1. Determine that a record of an account from a customer file is in arrears.
  2. Format the data into a form that is suitable for the report.

We have decided to code each task as a subprocedure. Conceptually, the module will look something like that shown in Figure 17.

Figure 17. Components of the ARRSRPT Module

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.

TIP

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.

Figure 18. Source for Subprocedure InArrears
 //--------------------------------------------------------------
 // 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.

 1 
All subprocedures begin and end with procedure specifications.
 2 
After the Begin-Procedure specification (B in position 24 of the procedure specification), you code a procedure interface definition. The return value, if any, is defined on the PI specification. Any parameters are listed after the PI specification.
 3 
Any variables or prototypes that are used by the subprocedure are defined after the procedure interface definition.
 4 
The return value, if specified, is returned to the caller with a RETURN operation.
 5 
If the record is not in arrears, the subprocedure returns '0' to the main procedure.

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.

Figure 19. Source for Subprocedure FmtCust
 //--------------------------------------------------------------
 // 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.

Figure 20. Source for module FMTPROCS, containing subprocedure FmtAddr.
    //=================================================================
    // 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.



[ Top of Page | Previous Page | Next Page | Contents | Index ]