The cycle is still a part of RPG, and while you are not using it in your RPG programs it is still there lurking. I have always wished there was a way to be able to turn it off, and allow RPG to be linear. In recent releases this has started to happen.
With the file definitions in all free RPG it is not possible to code a file as primary or secondary, or to define matching records and level break indicators. But the cycle is still there. For example, the program below uses the cycle to read all the records from a file:
dcl-f TESTFILE keyed ; read TESTFILER ; if (%eof) ; *inlr = *on ; dsply 'End of file' ; else ; dsply FLD001 ; endif ;
There are four records in TESTFILE, therefore, the output produced looks like:
DSPLY 1 DSPLY 2 DSPLY 3 DSPLY 4 DSPLY End of file
So while you would expect my program of nine lines to be very small, it is not as all of the RPG cycle stuff is included too. If I could create linear programs I could create smaller program objects.
IBM i 6.1 gave us the MAIN and NOMAIN keywords for the control options/H-specs. When the object is compiled, and one of these keywords is present, none of the RPG cycle functionality is included within it.
NOMAIN should be placed at the top of the source member for external subprocedures, as when it is compiled the module produced does not contain a Program Entry Procedure, PEP, which means it is not a callable program.
ctl-opt nomain ; /define GetEmployeeName /include devsrc,testrpg_c /undefine GetEmployeeName dcl-proc GETEMPLOYEENAME export ; dcl-pi *n char(85) ; EmplNbr char(10) options(*nopass) value ; end-pi; end-proc ;
If you are interested in learning more about coding subprocedures read the post More about subprocedures.
Coding a MAIN procedure is pretty much the same as coding any other procedure. It has a Procedure prototype (DCL-PR), Procedure interface (DCL-PI), and the Procedure (DCL-PROC) itself:
What I feel is the biggest limitation of using a MAIN is that it is not possible to return a value to the calling program.
01 ctl-opt main(Main) ; 02 dcl-pr Main extpgm('TESTRPG') ; 03 *n char(1) ; 04 end-pr ; 05 dcl-proc Main ; 06 dcl-pi *n ; 07 PassedKey char(1) ; 08 end-pi ; 09 dcl-f TESTFILE keyed ; 10 dcl-ds TestData likerec(TESTFILER) ; 11 chain Passedkey TESTFILER TestData ; 12 if (%found) ; 13 dsply TestData.FLD003 ; 14 else ; 15 dsply 'Key not found' ; 16 endif ; 17 end-proc ;
The first thing to notice is the control option, line 1, MAIN followed by the name I am going to call the procedure Main.
When defining the Procedure prototype I must use the EXTPGM keyword, line 2, to contain the name of the program that this procedure is contained within, TESTRPG. I will be passing a one character parameter to the procedure, line 3, which I am not naming here. And the prototype ends on line 4.
The start of the Procedure is marked by the DCL-PROC, line 5. I have not bothered to give the name of the procedure in the Procedure interface, line 6, so I have to need to have *N there to indicate that I am not. On line 7 I have given the passed parameter a name, and the interface ends on line 8.
Line 9 is a file definition. If a file is defined within a procedure I have to read the file into a data structure. On line 10 I define the data structure using the LIKEREC keyword. This defines all the fields in the record format, TESTFILER, to be subfields in the data structure.
Line 11 shows the CHAIN operation using the passed parameter as the key. The retrieved record is moved into the data structure, which is given after the record format or file name. As this is only one move from the input buffer this is faster than moving the individual fields from the input buffer, although with many files the difference is negligible.
All of the subfields in the data structure automatically qualified, see line 13.
The procedure ends on line 17. Notice how there is no RETURN operation code and no *INLR.
When compiled this program will contain no RPG cycle.
The fixed format definition equivalent of this program would look like:
01 H main(Main) 02 D Main PR extpgm('TESTRPG') 03 D 1 04 P Main B 05 FTESTFILE IF E K DISK 06 D PI 07 D PassedKey 1 08 D TestData DS likerec(TESTFILER) /free 09 chain Passedkey TESTFILER TestData ; 10 if (%found) ; 11 dsply TestData.FLD003 ; 12 else ; 13 dsply 'Key not found' ; 14 endif ; /end-free 15 P E
If you want to learn more about all free RPG you should check out the following posts:
- File definition in RPG all free
- Defining variables in RPG all free
- Defining Procedures in RPG all free
- Mixing it up with RPG all free
You can learn more about these on the IBM website:
This article was written for IBM i 7.2, and it should work with 6.1 and greater too.