• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • Variable Procedure Calls in Free-Format RPG

    March 31, 2010 Jon Paris

    Note: The code accompanying this article is available for download here.

    In my previous tip, I detailed the prototyping technique required to enable program calls in free-format RPG to use a variable to control the target program. These days, most modern RPGers make extensive use of subprocedures, so can the same techniques be applied to procedure calls?

    The simple answer is “yes,” but the technique is slightly more complex than that used for program calls. The reason for this is unlike program calls, procedure calls normally have to be resolved at program creation time, i.e., during the binding step. There are ways around this, one of which we will touch on later in this tip, but for now let’s work on the basics.

    The “secret ingredient” involved in this process is a procedure pointer. Take a look at the prototype for MyProc at (A) in the code extract below.

          // Prototype for dynamic procedure call
    (A)  D MyProc          Pr                  ExtProc(procPtr)
    
          // Prototypes for procedures to actually be called
         D Proc1           Pr
    
         D Proc2           Pr
    
         D procList        DS
    (B)  D   ptrToProc1                    *   ProcPtr Inz(%PAddr(Proc1))
    (C)  D   ptrToProc2                    *   ProcPtr Inz(%PAddr('PROC2'))
         D   ptrToNothing                  *   ProcPtr Inz
          // Redefine pointers as an array
    (D)  D   ptrArray                      *   Overlay(procList)
         D                                     ProcPtr Dim(3)
    
         D procPtr         S               *   ProcPtr Inz
         D procNumber      S              3I 0
    
         D exit            C                   99
         
          // Status codes for Monitor/On-Error tests
         D arrayIndexErr   C                   121
         D callFail        C                   202
    
          /Free
           // Ask for identifier of first procedure to call
           Dsply ('Enter procedure to call (1 - 3 or 99 to exit)')
                  ' ' procNumber;
    
           DoU (procNumber = exit);
    
             Monitor;
    (E)        procPtr = ptrArray(procNumber);
               MyProc();
             On-Error arrayIndexErr;
               Dsply ('Value of ' + %Char(procNumber)
                      + ' Not in range 1 to ' + %Char(%Elem(ptrArray)));
             On-Error callFail;
               Dsply ('Error detected when calling procedure ' + 
                      %Char(procNumber));
             EndMon;
    

    Notice that, as with the prototype for the dynamic program calls, the parameter to the keyword does not have quotes around it. As a result the compiler will treat the name as identifying a procedure pointer and will use the content of pointer to supply the call target.

    Simply defining the procedure pointer, of course, is not enough. We need to be able to load it with a value that points to the specific procedure that we want to call. RPG’s Built-in-function (BIF) %PADDR is used to obtain a pointer to a specified procedure. It can either reference a prototype name as at (B), or directly specify the procedure name as at (C). Note that when specifying the actual procedure name it is vital that you use the correct case for the name. Using ‘Proc2’ would not have worked. Using this approach, all of the procedure pointers must be able to be resolved at program creation time just as if we were directly invoking them via their individual prototypes.

    In my example I have defined the required procedure pointers in the data structure procList and then remapped them as array ptrArray (D). This allowed me to test the process by simply entering a number from 1 to 3. Notice that no procedure address was assigned to the third pointer so that I could test for a call failure.

    Before the call can be made, the required procedure pointer must be loaded. This takes place at (E), and is immediately followed by the procedure call itself. In this example there are two possible error conditions that could occur within the MONITOR block. The array index entered could be out of range, or we may have used an invalid procedure pointer–in my case the third entry in the array.

    In my example program I defined the subprocedures within the program itself, but the technique would be no different if they were within a service program or a separate module object.

    My example procedures do not use parameters, but it is important to understand that using this technique will normally rely on all the procedures involved having a common parameter structure. There are techniques that can avoid this requirement, but it is certainly a good idea when designing such a dynamic system to start from that assumption.

    More Flexible?

    Can we make this technique even more flexible? Yes we can. The approach that I am about to describe uses a regular program (*PGM) to provide the functionality of a service program (*SRVPGM), but with one important exception. By incorporating the technique described in the previous tip, we can dynamically call this “service program” and therefore, by changing the name of the program we call, change the set of procedures that we activate.

    There are a number of potential uses for such techniques, for example I have used it as part of a dynamically managed authority-based menu system. For now though I’ll simply focus on the technique and leave it to your imagination as to the uses you might put it to.

    Please note that the sample programs I have supplied to demonstrate this technique do not have a direct practical application. They are simply intended as a “geek’s playground” for those of you who like to explore ways of extending your programming toolkit. That said, let’s start by looking at some code extracts from the “service program” itself. Much of this should look familiar, as it is derived from the earlier example.

    At (F) you can see that the program’s procedure interface defines a parameter that is specified as being a clone of the procList data structure. The actual list of initialized procedure pointers can be seen at (G). Basically, it is the same code as we saw in the previous example.

     (F)  D ServProcs       PI
         D   procsAvail                        LikeDS(procList)
    
          // Prototypes for procedures
         D Proc1           Pr
    
         D Proc2           Pr
    
    (G)  D procList        DS
         D   ptrToProc1                    *   ProcPtr Inz(%PAddr(Proc1))
         D   ptrToProc2                    *   ProcPtr Inz(%PAddr('PROC2'))
         D   ptrToNothing                  *   ProcPtr Inz
    
          /Free
    
           // Set list of available procedures and return them to caller
    (H)    procsAvail = procList;
    
           Return;
    

    The entire main line logic of the program consists of just the two lines beginning at (H). It copies the program’s list of procedure pointers to the caller’s parameter and then returns to the caller. Notice that we do not want to set on LR; we want the program to remain active. If this were a real working program, I would probably have included a count of the number of active entries in the list in the data structure.

    In many cases it is also desirable to include some form of ID for each of the procedures so that the caller does not have to be aware of the sequence in which the procedures are defined but can seek out the procedure it needs by using its ID. I have used this approach in programs designed to make calculations based on formulas in text strings.

    Having looked at the “service program,” let’s now take a quick look at how it is initially invoked by the program that wants to use its services. As soon as this initial program call has been made, the procedure pointers are available so that procedure calls based on those pointers can now be made. Of course these pointers can be passed from program to program so that the procedures can be called from anywhere within the activation group.

    At (I) you can see the definition for the “service program” prototype. In real life this would be incorporated via a /COPY. Notice that the procList data structure incorporates the array definition for ptrArray. The data structure, and therefore the array of procedure pointers, is populated by the call at (J).

          // Prototype for dynamic call to "Service Program"
    (I)  D ServProgram     Pr                  ExtPgm(servProg)
         D   procList                          LikeDS(procList)
          ...
         D procList        DS
         D   ptrToProc1                    *   ProcPtr
         D   ptrToProc2                    *   ProcPtr
         D   ptrToNothing                  *   ProcPtr
          // Redefine pointers as an array
         D   ptrArray                      *   Overlay(procList)
         D                                     ProcPtr Dim(3)
          ...
           // Call "Service Program" to obtain list of procedures
    (J)    ServProgram(procList);
          ...
    

    The balance of the code is the same as in my first example so I will not comment further on it here.

    Play Time

    The full source code used in these examples is available here. I encourage you to play with them. If you have any questions about the techniques discussed here please email me through Ted Holt via the IT Jungle Contact page.

    Jon Paris is one of the world’s most knowledgeable experts on programming on the System i platform. Paris cut his teeth on the System/38 way back when, and in 1987 he joined IBM’s Toronto software lab to work on the COBOL compilers for the System/38 and System/36. He also worked on the creation of the COBOL/400 compilers for the original AS/400s back in 1988, and was one of the key developers behind RPG IV and the CODE/400 development tool. In 1998, he left IBM to start his own education and training firm, a job he does to this day with his wife, Susan Gantner–also an expert in System i programming. Paris and Gantner, along with Paul Tuohy and Skip Marchesani, are co-founders of System i Developer, which hosts the new RPG & DB2 Summit conference. Send your questions or comments for Jon to Ted Holt via the IT Jungle Contact page.

    RELATED STORY

    Variable Program Calls in Free-Format RPG



                         Post this story to del.icio.us
                   Post this story to Digg
        Post this story to Slashdot

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags:

    Sponsored by
    Computer Keyes

    Fax Directly from your IBM i

    KeyesFax is a full function automated IBM i fax system. Spooled files are burst by fax number and auto transmitted with overlays.  It combines both a send and receive facsimile processing system with a complete image package.

    The fax software will edit, send, receive, display, print, and track fax documents or images using any standard IBM i without additional expensive hardware, software or subscriptions.

    Computer Keyes has been developing Software Solutions since 1978!

    www.computerkeyes.com

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Sponsored Links

    Help/Systems:  Robot/CONSOLE monitors System i resources automatically
    Northeast User Groups Conference:  20th Annual Conference, April 12 - 14, Framingham, MA
    COMMON:  Join us at the annual 2010 conference, May 3 - 6, in Orlando, Florida

    IT Jungle Store Top Book Picks

    Easy Steps to Internet Programming for AS/400, iSeries, and System i: List Price, $49.95
    The iSeries Express Web Implementer's Guide: List Price, $49.95
    The System i RPG & RPG IV Tutorial and Lab Exercises: List Price, $59.95
    The System i Pocket RPG & RPG IV Guide: List Price, $69.95
    The iSeries Pocket Database Guide: List Price, $59.00
    The iSeries Pocket SQL Guide: List Price, $59.00
    The iSeries Pocket Query Guide: List Price, $49.00
    The iSeries Pocket WebFacing Primer: List Price, $39.00
    Migrating to WebSphere Express for iSeries: List Price, $49.00
    Getting Started With WebSphere Development Studio Client for iSeries: List Price, $89.00
    Getting Started with WebSphere Express for iSeries: List Price, $49.00
    Can the AS/400 Survive IBM?: List Price, $49.00
    Chip Wars: List Price, $29.95

    Source Technologies Now Supports System i with MICR Printer IBM Holds i 6.1 Prices Steady, Slashes Application Server Fees

    Leave a Reply Cancel reply

Volume 10, Number 12 -- March 31, 2010
THIS ISSUE SPONSORED BY:

WorksRight Software
ProData Computer Services
Northeast User Groups Conference

Table of Contents

  • Variable Procedure Calls in Free-Format RPG
  • Global Temporary Tables and Host Variables
  • Implementing 128-Character Passphrases in i/OS

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • IBM Unveils Manzan, A New Open Source Event Monitor For IBM i
  • Say Goodbye To Downtime: Update Your Database Without Taking Your Business Offline
  • i-Rays Brings Observability To IBM i Performance Problems
  • Another Non-TR “Technology Refresh” Happens With IBM i TR6
  • IBM i PTF Guide, Volume 27, Number 18
  • Will The Turbulent Economy Downdraft IBM Systems Or Lift It?
  • How IBM Improved The Database With IBM i 7.6
  • Rocket Celebrates 35th Anniversary As Private Equity Owner Ponders Sale
  • 50 Acres And A Humanoid Robot With An AI Avatar
  • IBM i PTF Guide, Volume 27, Number 17

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle