fhg
Volume 10, Number 12 -- March 31, 2010

Variable Procedure Calls in Free-Format RPG

Published: March 31, 2010

by 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


Sponsored By
WORKSRIGHT SOFTWARE

Do you need area code information?
Do you need ZIP Code information?
Do you need ZIP+4 information?
Do you need city name information?
Do you need county information?
Do you need a nearest dealer locator system?

We can HELP! We have affordable AS/400 software and data to do all of the above. Whether you need a simple city name retrieval system or a sophisticated CASS postal coding system, we have it for you!

The ZIP/CITY system is based on 5-digit ZIP Codes. You can retrieve city names, state names, county names, area codes, time zones, latitude, longitude, and more just by knowing the ZIP Code. We supply information on all the latest area code changes. A nearest dealer locator function is also included. ZIP/CITY includes software, data, monthly updates, and unlimited support. The cost is $495 per year.

PER/ZIP4 is a sophisticated CASS certified postal coding system for assigning ZIP Codes, ZIP+4, carrier route, and delivery point codes. PER/ZIP4 also provides county names and FIPS codes. PER/ZIP4 can be used interactively, in batch, and with callable programs. PER/ZIP4 includes software, data, monthly updates, and unlimited support. The cost is $3,900 for the first year, and $1,950 for renewal.

Just call us and we'll arrange for 30 days FREE use of either
ZIP/CITY or PER/ZIP4.

WorksRight Software, Inc.
Phone: 601-856-8337
Fax: 601-856-9432
E-mail: software@worksright.com
Web site: www.worksright.com


Senior Technical Editor: Ted Holt
Technical Editor: Joe Hertvik
Contributing Technical Editors: Erwin Earley, Brian Kelly, Michael Sansoterra
Publisher and Advertising Director: Jenny Thomas
Advertising Sales Representative: Kim Reed
Contact the Editors: To contact anyone on the IT Jungle Team
Go to our contacts page and send us a message.

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


 
The Four Hundred
Power7: Upgrade or Sidestep, Start Planning Now

IBM Flexes Java Muscles on Power7 Iron

IBM Looks Back on 2000s, Sets Sites on Next Decade

As I See It: A Different Currency

Madoff's RPG Coders Indicted in Ponzi Scam

Four Hundred Stuff
CoralTree Toolkit Streamlines CGIDEV2 Web App Development

Info Builders Updates Dashboarding Solution

Unitrends Adds Cloud-Based Storage to Backup Offering

HiT Bolsters Data Synchronization Tool

LTO Tape Drives: More than 3 Million Served

Four Hundred Monitor
Four Hundred Monitor's
Full iSeries Events Calendar

System i PTF Guide
March 27, 2010: Volume 12, Number 13

March 20, 2010: Volume 12, Number 12

March 13, 2010: Volume 12, Number 11

March 6, 2010: Volume 12, Number 10

February 27, 2010: Volume 12, Number 09

February 20, 2010: Volume 12, Number 08

TPM at The Register
Ex-contender for top IBM job pleads guilty on securities charges

AMD claims no premium for four-way chips

AMD draws x64 battle lines with 'Magny-Cours'

Intel Xeon 5600 invade big name servers

IBM: Mainframe emulator part of a conspiracy

Sun grows Oracle but stunts profits

HPC propped up server sales in 2009

Reseller exposes Opteron 6100 speeds and prices

QLogic spans Dell's InfiniBand

Red Hat all black in Q4

HP juices gamestations with Intel 'Westmeres'

Dell mainstreams cloud servers

THIS ISSUE SPONSORED BY:

WorksRight Software
ProData Computer Services
Northeast User Groups Conference


Printer Friendly Version


TABLE OF CONTENTS
Variable Procedure Calls in Free-Format RPG

Global Temporary Tables and Host Variables

Implementing 128-Character Passphrases in i/OS

Four Hundred Guru

BACK ISSUES




 
Subscription Information:
You can unsubscribe, change your email address, or sign up for any of IT Jungle's free e-newsletters through our Web site at http://www.itjungle.com/sub/subscribe.html.

Copyright © 1996-2010 Guild Companies, Inc. All Rights Reserved.
Guild Companies, Inc., 50 Park Terrace East, Suite 8F, New York, NY 10034

Privacy Statement