Guru: Refactoring RPG – Indicators
July 30, 2018 Ted Holt
Occasionally I hear someone comment about how terrible indicators are. I don’t think they’re bad. Indicator-laden RPG helped me graduate debt-free with a computer science degree and housed, clothed, and fed my family for several years. I prefer to say that indicators were good for their time, but now we have better programming techniques that I much prefer to use.
Refactoring code to reduce or even eliminate the use of predefined indicators (not indicator variables) can pay off big in benefits. The fewer indicators a program uses, the easier it tends to be to read, understand, modify, and debug that program. Let’s consider some ways to reduce indicator usage.
But first, let me clarify some terminology. When I say RPG III, I mean not only the variety that ran on the System/38, but also the native variety commonly called RPG/400. When I say RPG IV, I mean the current version of RPG, which some people habitually call ILE RPG.
- Remove unused indicators.
I hate to mention this, as it seems too obvious, but step number one is to identify and remove indicators that are set but never referenced. This is easily done, as the compiler listing points them out. Here’s a line that could come from an RPG II or RPG III program.
C SETOF 414243
Here are pieces of the compiler listing.
Indicator References: INDICATOR REFERENCES (M=MODIFIED D=DEFINED) LR 200M 41 100M 300 * 7031 42 100M 43 100M 400 * QRG7031 Severity: 00 Number: 1 Message . . . . : The Name or indicator is not referenced.
The compiler has flagged the unused 42 indicator with message ID QRG7031.
- When you change the setting of an indicator, do something with the indicator right then, not later.
I heard this somewhere 30 or so years ago when I was working in a System/36 shop, and to quote Robert Frost, that has made all the difference. If you don’t remember anything else from this article, remember this sage bit of advice.
In this code example, notice the use of indicator 21.
C** HILOEQ C BALDUE COMP 250.00 21 21 . . . many lines omitted . . . C 21 EXSR CALCDT . . . many lines omitted . . . C 21N22 MOVE 'Y' LAST 1 . . . many lines omitted . . . O 21 34 '*'
Indicator 21 calls a subroutine, conditions the assignment of a value to a variable, and prints an asterisk on a report when the balance due field is at least 250 dollars. Many lines, perhaps hundreds or even thousands, of calculations and output specs lie between the place where the indicator was set and the places where it is referenced.
Here’s a better way:
C BALDUE COMP 250.00 21 21 C MOVE *BLANK BSTAT 1 C 21 MOVE '*' BSTAT . . . many lines omitted . . . C BSTAT IFEQ '*' C EXSR CALCDT C END . . . many lines omitted . . . C BSTAT IFEQ '*' C N22 MOVE 'Y' LAST 1 C END . . . many lines omitted . . . O BSTAT 34
The BSTAT variable takes over the function of indicator 21. As soon as the BSTAT variable is assigned a value, indicator 21 becomes irrelevant.
From there it’s only one short step to removing the indicator entirely.
C BALDUE IFGE 250.00 C MOVE '*' BSTAT C ELSE C MOVE *BLANK BSTAT C ENDIF
I used an O spec in this example, but this replacement technique applies equally to externally-described printer files and display files. Not this:
A R DETAIL SPACEB(1) . . . lines omitted . . . A 21 34'*'
A R DETAIL SPACEB(1) . . . lines omitted . . . A BSTAT 1A 34
Use of this technique has allowed me to write many programs that used a single numbered indicator for every CHAIN, READ, READP, READE, READPE, READC, LOOKUP, etc.
- Convert to RPG IV if possible.
The Convert RPG Source (CVTRPGSRC) command makes quick work of conversion from RPG III to RPG IV. Since RPG IV is the most powerful of all the RPG family members, it stands to reason that you have more indicator-replacement techniques at your disposal. Consider one of the most heavily-used op codes, the CHAIN operation.
In RPG II and RPG III, you use a resulting indicator in the “HI” position to indicate a not-found condition.
*** HILOEQ C VENDOR CHAINAPVENDOR 75 C *IN75 IFEQ *OFF
If you convert this program to RPG IV, you can use the %FOUND built-in function instead of an indicator.
C VENDOR CHAIN APVENDOR C IF %FOUND(APVENDOR)
- Sometimes you may have to refactor RPG II or III for a while before conversion to RPG IV becomes worthwhile.
For instance, I’d prefer to work out a lot of conditioning indicators in RPG II or III, only because RPG IV only has room for one conditioning indicator in a C spec. That is, this:
C 31 48N17 CANN55 MOVE 'X' FACTR
May be easier to refactor than this:
C 31 CAN 48 CANN17 CANN55 MOVE 'X' FACTR
Or maybe not. Refactoring is a process, and there’s no one correct way to refactor a program. Many paths are equally valid.
- If you can’t eliminate an indicator, give it a self-documenting name. Instead of this:
If *In25; // Process account charges EndIf;
dcl-c validAccount_25 const(25); If *In(validAccount_25); // Process account charges EndIf;
- RPG II does not allow you to treat indicators like data, as RPG III and IV do, but you can get close.
Look at the use of indicator 33 in this code.
C** HILOEQ C BCUSNO CHAINCUST 33 C 33 MOVE . . . C 33 MOVE . . . C 33 EXSR SUBREF C 33 MOVE . . . C N33 Z-ADD. . .
If SUBREF does not modify indicator 33, and none of the routines that SUBREF calls modifies indicator 33, and none of the routines that are called from routines that SUBREF calls modifies indicator 33, ad infinitum, then the following is equivalent.
C** HILOEQ C BCUSNO CHAINCUST 33 C MOVE '0' #IN33 1 C 33 MOVE '1' #IN33 C #IN33 IFEQ '1' C MOVE . . . C MOVE . . . C EXSR SUBREF C MOVE . . . C ELSE C Z-ADD. . . C END
While this is not stellar code by modern standards, it’s a step closer to the distant and eventual goal, which to me usually means modern free-form calculation specifications.
I hope this gives you some techniques you can use to modernize the valuable code that runs your organization. Even more, I hope this article and my other refactoring articles give you hope that there are still many years of useful service in the software upon which the people in your organization depend for their livelihood.