I'm trying to get experience in RPGLE and IBM i stuff and constantly learning. Since most code in the wild seems to be classic positional, I'll stick with this to get used to it. So I would rather not use /free — /end-free stuff. Incidentally, I'm doing this on an old 9401-150 with only V4R5.
TL;DR: How can I get a return value back from an external called ILE program (with it's own MAIN section, that is, it's standalone in itself) in it's own activation group (*NEW
) to the callee?
I have a subfile program ready and running fine. I want to call an external program to handle requests by OPT value in the subfile. So I defined a PR in the D-Specs of the callee:
DROEDETPG PR EXTPGM('ROEDETPG')
DC_MODE LIKE(MODE)
DC_TYP LIKE(TYP)
Later, I call the Program which also works fine.
C SELECT
C OPT WHENEQ '2'
C MOVE 'CHG' MODE
C CALLP ROEDETPG(MODE:TYP)
C ENDSL
This is the entry point in the called program:
C *ENTRY PLIST
C PARM C_MODE 3
C PARM C_TYP 16
Now, maybe the record I want to change is already locked. So, I'm using CHAIN(E)
in the external program and get %STATUS
from the PF after the CHAIN
. The status value for that is 1218 and I want to get this value back to the calling program, so it may use the message line to tell the user, the record is locked and unavailable at the moment.
All I could find online is to prototype the call and define a call interface (PI) which is possibly applicable to procedures only.
So I thought of a "temp file" as I'm used to in Bash and C on Unix/Linux for that purpose. There seems to be no mktemp()
equivalent, but I can create files with the same name in QTEMP. This applies to a file type *DTAARA
. Unfortunately (expectable?) this file is visible only to the calling program. Maybe could create a global keyed *DTAQ with SENDERID(*YES)
but maybe this is overkill?
Why don't I put the functionality in the external program into a bunch of functions and use CALLP
? Well, I'm still learning. I decided to move stuff from Subroutines out of the main source to end the constant hassle with state changes involved when subroutines do stuff. When a SR does a READ, the database pointer points to another record which brings a multitude of erratic behavior when continuing the subfile stuff.
Plus, global variables (field content) get overwritten which adds more code to move content aside, save the key value for the DB, call the SR and restore variables again and do a SETLL
to get back to the state where I was before. I expect this to be easier but maybe I'm still too much a rookie regarding ile rpg.
I'm open for other suggestions regarding how to avoid my underlying problem (file pointer and global vars) properly.
Because parameters in ILE are passed by reference, one easy way to get one or more return values back from an external program is to just define them in your prototype and your called programs parameters. Coming from other programming environments, this is a little strange to me as I have been brainwashed into BYVAL
not BYREF
for so many years now, but really that is all a function return value ever is, a value on the stack that gets passed back to the caller. Some languages would define these as out
parameters, but they are actually inout
that you are treating like an out
.
DROEDETPG PR EXTPGM('ROEDETPG')
DC_MODE LIKE(MODE)
DC_TYP LIKE(TYP)
DC_ERR 7
C *ENTRY PLIST
C PARM C_MODE 3
C PARM C_TYP 16
C PARM C_ERR 7
One advantage to this method is that rather than return one value as RPGLE subprocedures do and functions do in most programming languages, you can return as many values as you need for a given task without defining a data structure to hold them all in.
QTEMP is a great library to use, but I think creating a file is overkill for what can be done with simple parameter passing. I do think you may have some misconceptions about QTEMP, though. It is defined on a per-job basis, so if you write something there (in a file or in a userspace object, which can be more convenient than a file if you are comfortable with pointers), it will absolutely be there for other programs running in that job to find until the job ends or it is explicitly deleted. Experiment a little to confirm this, and if necessary ask a separate question, because it really should be able to be used as shared storage for multiple programs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With