You've heard a lot about coding without indicators and perhaps even seen some code, but this article will show how the techniques can make you a better developer.
RPG programmers have a long and storied relationship with indicators. Certainly, it can be argued that without indicators there would be no RPG. However, it's been argued for some time now that we should write RPG with no indicators. In fact, some would argue that the only indicator you should ever see in an RPG program is *INLR: the last record indicator, which we still use to signal the end of a program. But does removing indicators have a real, practical benefit, or is it more of a theoretical exercise in "better programming"? This article provides a real example that proves the usefulness of the indicator-free programming style.
Change for the Sake of Change?
If you know me at all, you know I am not an advocate of change for the sake of change. I've been upset with some of IBM's moves over the years--no pun intended, even though the banishment of the MOVE opcode from the /free syntax is an example of a bad change in my opinion, and it's one that rankles deeply.
I often find myself at odds with compiler developers and computer science experts. On a mailing list awhile back, I had a serious debate with a couple of folks, one in which I took a minority position. The argument was somewhat esoteric and had to do with the fact that new, larger VARYING fields would require a change in how those fields were defined internally by the compiler. The crux of my argument was that the VARYING keyword should have a default that allows old programs to work without change, even if the compiler has to do magic under the covers for larger fields. I got a lot of grief for that position, but my point was simple: the compiler should always err on the side of making programming easier for everyday application developers rather than catering to those who push the envelope. And for once, I was vindicated; even though I was roundly criticized by the experts, in the end IBM implemented the VARYING keyword exactly how I suggested.
I bring this up as a preface to what I'm going to present to you in this article: a real, practical way to use indicator-free programming to make your job easier (especially in the brave new world of /free syntax). No doubt you've heard that indicators are bad and that we should strive to rid our programs of them, but in this article I'm going to take on one of the most difficult challenges to that notion--namely, how to handle display files--and show that indicator-free programming is not only possible, but even easier to code and maintain.
On to the Code
I've got a lot of ground to cover here, so I'm going to dispense with the pleasantries and get right down to business. Let me start off by explaining what I'm going to do and why, then move to the how, and then finish off with the ultimate benefits of the approach.
Of course, I have to lay the groundwork just a little, but I'll try to be brief. RPG has supplied us with a number of built-in functions (BIFs) that allow us to write RPG code without indicators. BIFs such as %eof come immediately to mind. However, no such advance has been made in the coding of Data Definition Specifications, or DDS. DDS is the way we describe display files, very much the way that HTML describes Web pages. And while much of the communication between the program and the display file is done through standard numeric and alphanumeric fields, many things are done primarily (and in some cases solely) through the use of indicators.
Three classes of interaction are most prevalent. First is the identification of which command key the user hit. This is so important that RPG actually used a separate set of indicators outside the normal 99 that were dedicated exclusively to command keys. Let me lead off by saying that one way to avoid the use of indicators for command keys is to use the workstation informational data structure (or INFDS) and the AID byte that is contained in that data structure. By testing the AID byte for specific hexadecimal values, you can determine which key was pressed, and this is a technique that many people advocate. I've never been partial to that technique; instead, I use numbered indicators 01 through 24 to handle the corresponding function keys (and then 25, 26, and so on to handle any additional command keys, such as Page Up and Page Down).
But Aren't Those Indicators?
Well, yes, they are! However, by exploiting a wonderful technique known as the indicator area, I can actually send those indicators back and forth without ever once having to reference them using the *IN syntax that we're trying to eliminate (I won't even dignify left-hand indicators). Instead, by using a combination of the INDARA and INDDS keywords (INDARA is used in the display file, while INDDS is used in the RPG program), I can create a data structure with named indicators to communicate with and control my display file.
Let's start with one of the most common uses of indicators, one that we can't get away from: using indicators for controlling a subfile. I often use a setup that looks like this:
A N31 SFLCLR
A 31 SFLDSPCTL
A 32 SFLDSP
Note that I'm using two indicators to control the subfile. First is 31, which if off clears the subfile and if on displays the subfile control record. In my experience, you're either doing one or the other; you clear the subfile prior to loading it and displaying it. In this case, though, I use a different indicator (32) to display the subfile itself. By doing this, I can choose to not display the subfile if it is empty but still display the subfile control record. This is a very common practice.
In the bad old days, I could use a MOVEA instruction to set the indicators to one of the three possible states. MOVEA '00' *IN,30 would set off both indicators 31 and 32, thus preparing the subfile to be loaded. After performing a WRITE to the subfile control record, I would set indicator 31 on in preparation for the WRITE that would actually display the screen. I would then load the subfile, and only if one or more records were written would I then set indication 32 on.
The code would be relatively simple:
C MOVEA '00' *IN(31)
C WRITE CTL1
C MOVE *ON *IN31
C DOW (some condition)
C WRITE SFL1
C MOVE *ON *IN32
C ENDDO
C WRITE CTL1
This would do what we needed to do. It would clear the subfile (the first write to CTL1) and then load the subfile, setting on first indicator 31 to display the control record, and then, if any records were to be written to the subfile, it would set on indicator 32. Note that in this case I'm skipping the code that actually loads the fields of the records and also the code that increments the relative record number for the subfile; I'm concentrating solely on the indicator code. You may recognize the use of the MOVEA opcode. This opcode is not available in /free syntax. And while the MOVE instruction can often be replaced with an appropriately coded EVAL opcode, there simply is no equivalent for the MOVEA.
However, I have a way around that limitation. By using an INDDS, I can do a little bit of magic. First, on the file specification I identify the INDDS data structure:
FIN0101D CF E WORKSTN SFILE(SFL1:Sfl1RR#)
F INDDS(WS_INDS)
Next, I specify the indicator data structure, in which I give indicators 31 and 32 names. More importantly, though, is the fact that I group them together and then create a couple of constants that I'll use to load the indicators:
DWS_INDS ds 99
D WS_SflCtlDsp 31 31n
D WS_SflDsp 32 32n
D WS_Sfl 31 32
D CWS_SflClear c '00'
D CWS_SflEmpty c '10'
D CWS_SflData c '11'
This not only creates the data structure that communicates between the RPG and the display file, but it gives names to the two indicators, 31 and 32, which control the clearing and display of the subfile. Next are the constants, which all start with CWS, each one defining a specific state of the two indicators. For example, CWS_SflClear is used when the subfile is to be cleared. By doing this, I can go to the following /free code:
WS_Sfl = CWS_SflClear;
write CTL1;
WS_Sfl = CWS_SflEmpty;
dow (something);
write SFL1;
WS_Sfl = CWS_SflData;
enddo;
write CTL;
As you can see, I don't have any ties to indicators 31 and 32 anymore; nor do I have to resort to hard-coded values such as '00' or even *ON and *OFF. Instead, I set the variable WS_Sfl to the appropriate state variable and then execute my WRITE instruction. Since WS_Sfl extends from positions 31 to 32 of the data structure, updating that value will set both of those indicators accordingly.
Other Uses of the Technique
So that gives you the basic technique. Identify your indicators intelligently, making sure that indicators that are to be set at the same time are adjacent numerically. This allows you to create a single field over multiple indicators and then set them all at once with a single EVAL statement.
But this technique is not limited to subfile control. Another situation I find useful is in just about any standard maintenance program:
A 4 5'Item Number'
A COLOR(BLU)
A XOITEM 15A B 4 25
A 41 DSPATR(RI PC)
A 61 DSPATR(PR)
A N61 DSPATR(UL)
A 6 5'Description'
A COLOR(BLU)
A XODESC 30A B 6 25
A 42 DSPATR(RI PC)
A 62 DSPATR(PR)
A N62 DSPATR(UL)
A 7 5'Manufacturer Code'
A COLOR(BLU)
A XOMANU 15A B 7 25
A 43 DSPATR(RI PC)
A 63 DSPATR(PR)
A N63 DSPATR(UL)
Here is a panel with three fields: item number, description, and manufacturer code. Item number is the key. I use this all the time: indicators from 41-59 are used to identify errors, while indicators 61-79 are used to protect fields. But, through the use of the INDDS, I can do a little magic:
DWS_INDS ds 99
(...)
D WS_Errors 41 60n DIM(20)
D WS_ProtKeys 61 61n DIM(1)
D WS_ProtData 62 80n DIM(19)
Note that I identify all the error indicators in one array, and then I split the protection indicators into two separate arrays, for key fields and data fields. This allows me to do several things. Here are some examples:
WS_Errors = *off; // Clear all errors
By setting the array to *OFF, all error indicators are cleared.
WS_Errors(2) = *on; // Identify field 2 as in error
Next, I can set an error using a simple field number. I could even use a named constant to make it more self-documenting:
WS_Errors(C_DESC) = *on; // Error in DESCRIPTION field
This is my favorite, though. It's now incredibly easy to protect the various fields, depending on what I am trying to do:
select;
when opcode = C_ADD or opcode = C_COPY;
WS_ProtKeys = *OFF;
WS_ProtData = *OFF;
when opcode = C_CHANGE;
WS_ProtKeys = *ON;
WS_ProtData = *OFF;
when opcode = C_DELETE or opcode = C_VIEW;
WS_ProtKeys = *ON;
WS_ProtData = *ON;
endsl;
So now, if I am adding or copying a record, both the key and data fields are input-capable. If I am changing a record, the key fields are protected, but the data fields are editable. And if I'm deleting or viewing a record, all fields are protected. That's a pretty slick way of doing things, I think.
Is This the Only Way to Get Rid of Indicators?
No, there are other techniques. Attributes can be specified using special program-to-system fields. Cursor position can be done the same way. But I like the simplicity of the INDDS data structure. By using consistent naming, I have created a skeleton program that will adapt itself to different numbers of key and non-key fields without any work on my part other than laying out the INDDS data structure properly.
Being Indicator-Free
As demand dictates, I'll write additional articles to touch on some other indicators you can get rid of without a lot of effort. But for now, you can begin your journey to an indicator-free programming style.
LATEST COMMENTS
MC Press Online