Dumping Subroutines: Breaking Up Is Hard To Do, Or Is It?

Next, we modify our procedure adding the code and description parameters and changing any reference to #sSel02 and #gCdsc to code and desc respectively.

      //  Procedure - $PrcCde       Process Code
     P $PrcCde         B
     D                 pi 
     D code                           3    value
     D desc                          30    value
     C                   If        code   <> ' '
     C                   Eval      VGVCLC =  ' '
     C                   Eval      vgdgre = ' '
     C                   Eval      VGLOT# =  #GLOT#
     C                   Eval      VGVAT# =  #GVAT#
     C                   Eval      #DVATD =  %dec(%char(%date(#GVDTE:*mdy)
     c                                       :*iso0):8:0)
     C                   Eval      VGVATC =  #GVATC
     C                   Eval      VGVATD =  #GVATD
     C                   Eval      VGVATM =  #GVATM
     C                   Eval      VGVATY =  #GVATY
     C                   Eval      VGCODE =  code
     C                   Eval      VGUSM  =  ' '
     C                   Eval      VGDTM  =  0
     C                   Eval      VGTMM  =  0
     C     #KVGN01A      Chain     VatNotesRec
     C                   If        Not %Found(VatNotes02)
     C                   If        code <> *blanks 
     C                   Eval      VGVCLC =  'C'
     C                   Eval      VGCDSC =  msdesc
     C                   Eval      VGUSC  =  #duser
     C                   Eval      VGDTC  =  %dec(%char(%Date():*iso0):8:0)
     C                   Eval      VGTMC  =  %dec(%char(%Time():*hms0):6:0)
     C                   Write     VatNotesRec
     C                   EndIf
     C                   else
     C                   Eval      VGCDSC =  desc
     C                   Eval      VGUSM  =  #duser
     C                   Eval      VGDTM  =  %dec(%char(%Date():*iso0):8:0)
     C                   Eval      VGTMM  =  %dec(%char(%Time():*hms0):6:0)
     c                   update    VatNotesRec
     C                   EndIf
     C                   Endif
     C***                   Clear                   #ssel02
     P                 E

Since #sSel02is replaced by code, we inactivate the clear statement forit. We also use the value keyword in the prototype and procedure interface to eliminate any chance of inadvertently changing an argument’s value while the procedure is executing. This makes a copy of the argument instead of pointing to the same variable, which is always a good practice unless you must pass large amounts of data multiple times.

Again, we modify our call to $PrcCde:

     Callp       $PrcCde(#sSel02:msdesc)

Or the /free equivalent:


Later, when we call our procedure to write production notes, we simply pass it different arguments:


Another extremely useful technique that’s possible through local subprocedures is using return values. By doing so, not only can you assign variables to the result of a subprocedure, you can use them in conditional logic, like an If statement for instance.

Cheese graders have more authority in maintaining grade codes than others. Consequently, the program to maintain grade codes should reflect that rule. By making the return value in a local procedure *on or *off, you can call a procedure to check if the user is a cheese grader in an If statement, select blocks or even in loops:

If  isGrader();
  //Do cartwheels… AND set your screen to allow things only graders should see

When the If statement executes, it calls the procedure isGrader.

     //Procedure - Checks if user is a cheese grader
     P isGrader        B
     FGrdMast01 if   e           k disk
     D                 pi              n
     D @GrdMast     e ds                  ExtName(GrdMast01:GrdMastRec:*input)
     D auth            s               n
       msecot = #cChGraderKey;
       mscode = #dUser;
       Chain (msecot:mscode) GrdMastRec @GrdMast;
       If %found(GrdMast01);
        auth = *on;

       Return auth;
     P                 E

In the prototype, the return value is specified on the same line as PR:

     D isGrader        PR              N

The local variable auth is initialized every time the procedure is called and terminates when the procedure ends. Another cool thing about the last example (at the risk of being beaten over the head by Bryan Myers with the RPG Style Guide) was the file declared in the procedure. Normally, I would have it declared in the main F specs, but I already had it declared there once. Essentially, this is the same as using the Rename keyword on an F spec. It has it’s own placeholder and terminates when the procedure finishes executing.

Finally, there is an example that my guess is a common routine for RPG programmers. Often, on a report or screen we need to retrieve a description from a file like the description from our item master. To do that, we Chain out to the item master using the item number as the key:

   Chain #fCmptOrig ItemMstRec;
   If %found(ItemMst01);
    #fDescOrig = itemdesc;
    #fDescOrig = ‘Not Found in Item Master Table’;

Sometimes we need to display more than one item—for instance, a finished good item and one or more components that make up that finished good. We could copy this code over and over again making slight changes to populate different fields on our display, or we could create a local procedure and only write the code once.

      //   Returns description when passed an item number
     P getDesc         B
     D                 pi            30
     D item#                          5  0 value
     D desc            s             30
       Chain item# ItemMstRec;
       If %found(ItemMst01);
        desc = itdesc;
        desc = 'Not Found in Item Master Table';

       Return desc;
     P                 E

Again, the return value is set in the same line as PR for the prototype and pi for the procedure interface. In many cases, a local variable of the same type and length as the return value will be required as well.

D             PR                30
D  itemNumber                    5  0 value

After it’s all set up, just call it passing different arguments each time.

      #fDescOrig = getDesc(#fCmptOrig);
      #fDescAlt = getDesc(#fCmptAlt);

Write Your Dear Subroutine Letter

The more we call getDesc, the more dividends we gain from our code, and any time I see the need to write something similar to what’s already in the code somewhere else, I know I’ve got a prime candidate for a local procedure. So make a clean break and write your Dear Subroutine letter today.

Brian Lannoye is a programmer at Masters Gallery Foods Inc., in Plymouth, Wis. He’s been working there as a programmer on the IBM i platform since June 2010. Brian regularly attends WMCPA meetings and is currently working toward a bachelor’s degree at Lakeland College Online.

comments powered by Disqus



2019 Solutions Edition

A Comprehensive Online Buyer's Guide to Solutions, Services and Education.

New and Improved XML-INTO

Namespace support makes the opcode a viable option

Authenticating on the Web

The finer points of OpenRPGUI, Part 1

The Microphone is Open

Add your voice: Should IBM i include open-source RPG tools?

IBM Systems Magazine Subscribe Box Read Now Link Subscribe Now Link iPad App Google Play Store
IBMi News Sign Up Today! Past News Letters