The New Basics: Indicators
September 19, 2012 Jon Paris
The history of indicators pre-dates even the earliest versions of RPG and takes us all the way back to the old tabulating machines, perhaps even earlier depending on how broad a definition one uses. What is without doubt, though, is that indicators in the sense of *INnn and *INLR were introduced to the RPG language as a vehicle for translating those old tabulator board programs.
RPG has grown a lot since those early days, and yet many people still write code using the old-style numbered indicators. LR? Well, we’re kind of stuck with that, but there hasn’t been a need to use numbered indicators in your RPG programs for a very, very long time. Not since V4R2 in fact, when named indicators were first introduced into the language. Of course, if you use program-described report files, you have little choice but to use the numbered indicators on the O-specs themselves, but in the calculations you do have a choice.
I covered some indicator basics in a previous article An Indicator By Any Other Name. I strongly suggest you review that article as a refresher. This article picks up where we left off, having established the following points:
*Indicators can be treated as an array. That is, *In55 and *In(55) reference the same indicator.
*The compiler understands that an indicator (named or otherwise) is a Boolean and can be tested as such.
/free If *In(55); . . . Endif /end-free
The above code can also be presented this way:
/free If Not *In55; . . . Endif /end-free
*Named constants can be used as array subscripts allowing names to be associated with indicators. For example, here’s one way to associate indicator 55 with a name: overCreditLimit_55.
d overCreditLimit_55... d c const(55) /free if *In(overCreditLimit_55);
* A logical expression can be assigned to an indicator. For example, the following expression sets indicator 55 on or off depending on the true/false result of the test.
/free *In(overCreditLimit_55) = ( orderTotal + currentBalance ) > creditLimit;
Now Let’s Take The Next Step
Indicators *In01 – *In99 are just a set of bytes in memory–consecutive bytes. So if we want to give names to all 99 indicators (or as many of them as we have used), we can do so by mapping the indicator array to a data structure of our own design. The following code demonstrates the basic idea. If pointers make you nervous, don’t worry. When used this way, you’ll never have to do anything with the pointer; the compiler does all the work for you. Here’s an example:
d pIndicators s * Inz(%Addr(*In)) d indicatorNames ds Based(pIndicators) d newPage_01 n d endOfFile_02 n // ... d errorFlags Overlay(indicatorNames: 55) d overCreditLimit_55... d n Overlay(errorFlags) d invalidCustomer_56... d n Overlay(errorFlags: *Next) d badPartNumber_57... d n Overlay(errorFlags: *Next)
Note that I defined the pointer first, followed by the indicator data structure. If I do it this way, it makes it easy for me to incorporate the basic setup in a /Copy member if I want to and then add my own application specific definitions to the data structure.
This example also makes use of a group field (errorFlags), which makes it possible for me to turn on or off a group of indicators very easily.
/free errorFlags = *Off; /end-free
Another way to show this would be:
/free errorFlags = *On; /end-free
I never did like the old approach of using MOVEA for this purpose. I find this way far better.
A similar approach can be used with indicators for a subfile, where a particular on/off combination is needed to display or clear the subfile. In this case I would use named constants with the appropriate “0” and “1” pattern to achieve the settings I needed. Here’s an example:
D pIn S * Inz(%Addr(*In)) D indicatorArray DS Based(pIn) // Input request indicators D exit_3 n Overlay(indicatorArray: 3) D sortPrCode_5 n Overlay(indicatorArray: 5) D sortDesc_6 n Overlay(indicatorArray: 6) // Output control indicators D sflControls_90_92... D 3a Overlay(indicatorArray: 90) // Constants to set subfile operations D clearSfl C '010' D displaySfl C '101' // Individual indicators shown for reference - not used in code D sflDspCtl_90 n Overlay(indicatorArray: 90) D sflClear_91 n Overlay(indicatorArray: 91) D sflDsp_92 n Overlay(indicatorArray: 92)
Now to set up the indicators to clear or display the subfile, I need only code a single line and, unlike MOVEA, what I am doing is very obvious. For example:
sflControls_90_92 = ClearSfl; sflControls_90_92 = DisplaySfl;
In fact, this is about the only time I ever use the old character style representations of indicator values.
INDDS: The Architected Approach To Naming Indicators
Back in V4R2 the RPG compiler folks decided that, in the absence of direct DDS support for indicator names. they should come up with an RPG-specific implementation. INDDS was the result. It stands for INDicator Data Structure and is simply added to the F-spec of the file (or files) that are to use the named data structure for their indicators. There are several important things to note about INDDS:
That said, let’s use a simplified version of the previous example to see how the whole thing works:
Example1 CF E WORKSTN INDDS(dispInds) d dispInds ds // ... d errorFlags Overlay(dispInds: 55) d overCreditLimit_55... d n Overlay(errorFlags) d invalidCustomer_56... d n Overlay(errorFlags: *Next) d badPartNumber_57... d n Overlay(errorFlags: *Next) /Free Clear errorFlags; ... overCreditLimit_55 = ( orderTotal + currentBalance ) > creditLimit;
To me this is the cleanest approach and can be useful when the limit of 99 indicators per file gets in the way. If you need to have multiple files sharing some of the indicators, but don’t want to have them share the same data structure, this can be handled quite nicely using EVAL-CORR (Evaluate Corresponding).
You just need to clone the relevant portions of (say) the display file data structure and add the keyword QUALIFIED to the new data structure definition. Then whenever you want to copy the state of the matching indicators from one data structure to the other you simply code an EVAL-CORR to copy them in the appropriate direction. The example below demonstrates this:
d dispInds ds // ... d errorFlags Overlay(dispInds: 55) d overCreditLimit_55... d n Overlay(errorFlags) d invalidCustomer_56... d n Overlay(errorFlags: *Next) d badPartNumber_57... d n Overlay(errorFlags: *Next) d printInds ds QUALIFIED // ... d errorFlags Overlay(printInds: 55) d overCreditLimit_55... d n Overlay(errorFlags) d invalidCustomer_56... d n Overlay(errorFlags: *Next) d badPartNumber_57... d n Overlay(errorFlags: *Next) /Free Eval-Corr printInds = dispInds;
It is important that you not change the names of the indicators or this won’t work. That’s the meaning of the “corresponding” part of the op-code. Only those fields with matching names and compatible data types will be copied.
Hopefully I’ve helped to update your understanding of modern indicator usage. If there are any other basic aspects of modern RPG programming you would like to see covered in this series please let me know.
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.