CONST Doesn’t Mean You Can’t Change It
October 20, 2015 Ted Holt
You can pass parameters to an RPG subprocedure in three ways: by reference, by read-only reference, and by value. (I have written about this before.) My favorite method is read-only reference. I use it as often as I can. Would you believe that it is possible to change the value of a variable that is passed to a subprocedure by read-only reference? Since you would probably do so only inadvertently, it’s good to understand how it can happen. To pass a parameter by read-only reference, include the CONST keyword in the procedure prototype and the procedure interface. D Process pr D inSection like(Section) const P Process b D pi D inSection like(Section) const If the program from which this code was taken tries to modify inSection within the Process subprocedure, like this: eval inSection = 'XX' The compiler heartlessly responds with message RNF5346: “The operation modifies the field, but the field cannot be modified.” And so the compilation fails. No, to modify inSection, you have to be sneaky. (Or in my case, stupid.) Here’s how it’s done. Assume a database table: create table Specs (Section char(2), Sequence dec(3), Position dec(3), Text char(50), primary key (Section, Sequence)) rcdfmt Spec; insert into Specs values ('BB', 1, 1, 'XXXXXXXXXXXXXXX'), ('BB', 2, 1, 'XXXXXXXXXXXXXXX'), ('BB', 3, 13, 'XXX'), ('BB', 4, 12, 'XXX'), ('BB', 5, 11, 'XXX'), ('BB', 6, 10, 'XXX'), ('BB', 7, 9, 'XXX'), ('BB', 8, 8, 'XXX'), ('BB', 9, 7, 'XXX'), ('BB', 10, 6, 'XXX'), ('BB', 11, 5, 'XXX'), ('BB', 12, 4, 'XXX'), ('BB', 13, 3, 'XXX'), ('BB', 14, 2, 'XXX'), ('BB', 15, 1, 'XXX'), ('AA', 1, 4, 'XXXXXXXXX'), ('AA', 2, 2, 'XX XX'), ('AA', 3, 1, 'XX XX'), ('AA', 4, 11, 'XXX'), ('AA', 5, 9, 'XXX'), ('AA', 6, 11, 'XXX'), ('AA', 7, 1, 'XX XX'), ('AA', 8, 2, 'XX XX'), ('AA', 9, 4, 'XXXXXXXXX'); Here’s a printer file: A R HEADER SKIPB(1) A 1'==BEGIN==' A R DETAIL SPACEB(1) A MESSAGE 80 1 A R TRAILER SPACEB(1) A 1'==END==' And finally, an RPG program: H dftactgrp(*no) actgrp(*new) option(*srcstmt: *nodebugio) FSpecs if e k disk FQAD0040P o e printer D Process pr D inSection like(Section) const /free *inlr = *on; read Spec; dow not %eof(Specs); write Header; Process (Section); write Trailer; enddo; return; /end-free P Process b D pi D inSection like(Section) const /free dow not %eof(Specs) and Section = inSection; clear Message; %subst(Message: Position) = Text; write Detail; read Spec; enddo; /end-free P Process e Notice the Process subroutine. Its purpose is obviously to print all the data for whichever section is specified in the inSection parameter. Just glancing at the data and this program, you would expect this two-page report: ==BEGIN== XXXXXXXXX XX XX XX XX XXX XXX XXX XX XX XX XX XXXXXXXXX ==END== < . . . imagine a page break here . . . > ==BEGIN== XXXXXXXXXXXXXXX XXXXXXXXXXXXXXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX ==END== But, no, you’d get a one-page report, like this: ==BEGIN== XXXXXXXXX XX XX XX XX XXX XXX XXX XX XX XX XX XXXXXXXXX XXXXXXXXXXXXXXX XXXXXXXXXXXXXXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX ==END== Here’s why. The main routine passes the address of the Section variable to the Process subprocedure. This means that inSection (in the subprocedure) and Section (throughout the entire program) are two names for the same spot in memory. When Process reads the Specs table, the system changes the value of Section, which is also the value of inSection. Consequently, the control break never occurs and the loop stops at end of file. Global variables cause a lot of grief and I try to avoid them, but in all fairness, passing lots of parameters between subprocedures can get mighty unwieldy. There are several ways to fix this problem. Perhaps the easiest is to pass the section ID by value. D Process pr D inSection like(Section) value P Process b D pi D inSection like(Section) value This change gives you the two-page report you expected. Here’s another way to change the value of a variable that is passed by read-only reference. I’ll modify the preceding program slightly in order to illustrate. H dftactgrp(*no) actgrp(*new) option(*srcstmt: *nodebugio) FSpecs if e k disk D Process pr D inSection like(Section) const D BR549 pr extpgm('BR549') D inSection like(Section) const /free *inlr = *on; read Spec; dow not %eof(Specs); Process (Section); read Spec; enddo; return; /end-free P Process b D pi D inSection like(Section) const /free BR549 (inSection); /end-free P Process e The program passes the address of Section to subprocedure Process, which knows the parameter as inSection. The subprocedure calls program BR549, passing the section value along by read-only reference. Now let’s take a look at BR549. pgm (&inSection) dcl &inSection *char 2 chgvar &inSection 'ZZ' return endpgm Even though the RPG program thinks the CL program won’t modify the parameter, the CL program has other ideas. In fact, the CL program doesn’t even know that the RPG program thinks the parameter won’t be modified. I highly recommend passing parameters by read-only reference, but be aware that the CONST keyword isn’t foolproof. RELATED STORY Parameter Passing and Performance
|