On 1/6/2015 1:38 PM, Jack Tucky wrote:
If I want to put this code in another program, how would I define the procedure there?
I have mixed emotions about trying this on a mailing list. The
following looks complicated, but plenty of people do this every day, so
it can't be all that bad.
If you've never written a subprocedure before, I'm very tempted to
advise you to (for the moment) put the idea of a service program aside,
and put the PR and PI in the same RPG source member and get used to how
they work.
The rationale for that is that there is really no other infrastructure
you need to use a procedure other than actually writing the code. It's
little more than a souped up subroutine, and everybody knows how to use
them. That gets one familiar with terminology (prototype, procedure
interface, local variable scope, etc.) and what little bit of mechanics
there are.
The alternate idea is that by going straight to a service program you
won't be remotely tempted to use global variables (from the mainline)
because there won't be any mainline for you to lean on! /Thinking/
about the procedure as a separate, independent block of code is very
useful, and it lets me think about the bits that sometimes get missed:
what about error handling? If the 'next number' is locked, how do I
tell the caller? What do I do when the number rolls over? What if the
caller is not authorised? Fails a trigger? Things like that; the stuff
that would make this generic, and not a fancy 'external subroutine'.
I re-read that and it doesn't look all that helpful. Maybe it'll make
more sense if you read the lot and come back to it.
So normally, there's a PR at the top of the program and a PI at the
bottom. The PR is the prototype - it tells the compiler what to expect.
That may seem silly until you want to put the procedure in a service
program and just call it from many mainline programs.
The way I do it is somewhat simplistic. I have one service program
where I keep all my 'utility' procedures like date conversions, text
conversions and so on. I call it UTILITIES.
The source for that is in QRPGLESRC, member UTILITIES. There are
several procedures in there. Each of those procedures specifies EXPORT
on the PI. The prototypes for all of these procedures are kept in
QPROTOSRC, member UTILITIES. I compile the QRPGLESRC member with CRTRPGMOD.
Going along with these source members is another: binder language. I
keep the binder language in QSRVSRC, member UTILITIES. This is used
when creating the service program to create a signature. Unless you
have a fairly complex environment, it makes sense to have just one
signature. Mine looks like this:
strpgmexp pgmlvl(*current) signature('Date utilities')
export symbol(date10to8)
...
endpgmexp
If I add a new procedure, it goes at the bottom of this QSRVSRC member.
I create the service program with CRTSRVPGM EXPORT(*SRCFILE) - I don't
ever use EXPORT(*ALL).
To make it easier for callers to consume this, I have a binding
directory called PROCEDURES. In that binding directory, I have SRVPGM
UTILITIES (ADDBNDDIRE or WRKBNDDIR).
To call this from an order entry program, that program would need these:
h bnddir('PROCEDURES')
/copy qprotosrc,utilities
...and the actual statement that calls the procedure:
ubirthdt = date10to8(birthdat);
The venerable RPG Redbook 'Who Knew You Could Do That' is still a good
place to start if you've never done a service program.
http://www.redbooks.ibm.com/abstracts/sg245402.html It took me repeated
reading / doing cycles before all of this became second nature, so don't
feel discouraged if it doesn't all come in one go.
--buck
On Jan 6, 2015, at 1:31 PM, Buck Calabro <kc2hiz@xxxxxxxxx> wrote:
On 1/6/2015 12:29 PM, Jack Tucky wrote:
I tried to code the PR/PI in my first RPG and it says I can't return a
value. Do I need something different? Sorry I'm so vague. I'm still not
where I need to be on procedures/service programs, etc.
In order to return a value, you need to tell the procedure interface
(and prototype) that. Here is an example of a procedure that takes a 10
character 'date' and converts it into an 8 digit number. The place
where we define what gets returned is on the PI spec; here it's '8s 0'.
The actual /value/ that gets returned is in the return statement (here,
unimaginatively called retVal).
* convert 'yyyy-mm-dd' to yyyymmdd
p date10to8 b
d date10to8 pi 8s 0
d date10 10a const
...
d retVal s 8s 0 inz
...
return retVal;
p e
You'd do something similar with your 'return the next number' procedure.
As an Amazon Associate we earn from qualifying purchases.