Guru Classic: The New Basics — Indicators
July 17, 2019 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 added 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.
Since I first wrote the original version of this tip back in 2012, RPG has experienced another major change with the introduction of completely free-form data decelerations. That made this tip an obvious choice for re-working as a “Classic”.
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 that one 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. For example the following are both perfectly valid methods of using indicator 55.
If *In(55); // . . . Endif; If Not *In55; // . . . EndIf;
- 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 the name: overCreditLimit_55.
dcl-c overCreditLimit_55 55; if *In(overCreditLimit_55); // . . . EndIf;
- 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.
*In(overCreditLimit) = ( 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:
dcl-s pIndicators Pointer Inz(%Addr(*In)); dcl-ds indicators Based(pIndicators); newPage_01 ind Pos(1) endOfFile_02 ind Pos(2); // errorFlags char(3) Pos(55); overCreditLimit_55 ind Pos(55); invalidCustomer_56 ind Pos(56); badPartNumber_57 ind Pos(57); end-ds;
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 defines a field (errorFlags), which makes it easy for me to turn on or off a group of error indicators very easily. Like so:
errorFlags = *Off;
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:
dcl-ds DisplayIndicators Based(pIndicators); // Input request indicators exit_3 ind Pos(3); sortPrCode_5 ind Pos(5); sortDesc_6 ind Pos(6); // Output control indicators sflControls_90_92 char(3) Pos(90); // Individual indicators shown for reference - not used in code sflDspCtl_90 ind Pos(90); sflClear_91 ind Pos(91); sflDsp_92 ind Pos(92); end-ds; // Constants to set subfile operations dcl-c clearSfl '010'; dcl-c displaySfl '101';
Using this approach then 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 to those who come after me. 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 for indicators.
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:
- When INDDS is used for a file, there is no longer any connection between the indicators used by the file and the standard *IN series. The device will only use the indicators defined in the specified data structure.
- Each file can specify its own unique data structure or a single data structure can be shared by multiple files.
- INDDS can only be specified for files defined with the INDARA (Indicator Area) keyword. I had hoped that over the years this restriction would be removed, but luckily I did not hold my breath waiting.
- Display files can be changed to use this keyword with no problems at all providing that you keep in mind that with INDARA files a CLEAR or RESET of a record format will not affect the indicators, only the data fields.
- The indicator array identified by the INDDS keyword is always 99 bytes long. The POS keyword provides an easy way to map the indicator number to the name.
That said, let’s use a simple display file and include the same error indicators that we used in the earlier example. This is how the file declaration and indicator data structure would be coded:
dcl-f DispFile WORKSTN(*EXT) USAGE(*INPUT:*OUTPUT) INDDS(dispIndicators); dcl-ds dispIndicators; // errorFlags char(3) Pos(55); overCreditLimit_55 ind Pos(55); invalidCustomer_56 ind Pos(56); badPartNumber_57 ind Pos(57); end-ds;
To me this is the cleanest approach to handling indicators and can be very useful when the limit of 99 regular indicators gets in the way. Suppose you need to have multiple files sharing some of the indicators but, in order to be able to use more than 99 indicators in total, 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 use an EVAL-CORR to copy them in the appropriate direction.
The example below shows both this approach and a simpler option which takes advantage of the fact that the display file error indicators and the indicators used by the O-Specs for printing are grouped into a single field.
dcl-ds printIndicators Qualified Based(pIndicators); // Print specific indicators newPage_01 ind Pos(1); endOfFile_02 ind Pos(2); // indicators common with display file errorFlags char(3) Pos(55); overCreditLimit_55 ind Pos(55); invalidCustomer_56 ind Pos(56); badPartNumber_57 ind Pos(57); end-ds; // Copy all common indicators from display file to print Eval_Corr printIndicators = dispIndicators; // Or just set print indicators from display file settings printIndicators.errorFlags = errorFlags;
It is important that you not change the names of the individual 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 foremost experts on programming on the IBM i platform. A frequent author, forum contributor, and speaker at User Groups and technical conferences around the world, he is also an IBM Champion and a partner at Partner400 and System i Developer. He hosts the RPG & DB2 Summit twice per year with partners Susan Gantner and Paul Tuohy.