This morning I got question from an old RPG3 chum of mine:
I know a couple of questions they will ask me, what are the different parms used on sub-procedures like *OMIT/*Nopass. Didn’t we use those any way? I thought we were writing programs that did or did not necessarily get parms depending what called them using those keywords? To be honest, I know I can look it up but what the bloody hell is a sub-procedure?
My answer started off little then got into the huge waffling email stage.
Anyway, I just decided to copy/paste it here for future reference. Bear in mind this is a very high level overview and not designed to get into the nitty-gritty of anything, but if it helps anyone out there then its #winning and thats the purpose of this blog:
*OMIT/*NOPASS makes parameters funky
*OMIT – Lets you send a parameter as a value or as *nulls
Lets you use the *OMIT keyword when calling you program. So, if you just dont want to pass anything, you can say *OMIT – I think it just passes *NULLS and in the program that is being called you would say something like
if %addr( parm1 ) = *NULL ; parmvalueinprogram = 'something'; else ; parmvalueinprogram = 'parm1'; endif ;
*NOPASS – lets you just neglect to pass anything at all
Don’t even bother. I think it just passes *NOTHING and in the program that is being called you would say something like
select ; when %parms() = 0 ; parmvalueinprogram = 'something'; when %parms() = 1 ; parmvalueinprogram = 'parm1'; endsl;
Sub-Procedures are very cool.
Probably my favorite RPG enhancement over the years. Coming from RPG3/400 to RPGLE this is something you will definitely need to learn. It’s been used extensively everywhere I’ve worked since leaving the boundaries of ‘JBA’ code and Green screen land 🙂
RPG Sub procedures The Basics
You can create two kinds of procedures in RPG: a main procedure (think: mainline) and a subprocedure.
A main procedure uses the RPG cycle. It is specified in the main source section. You do not need to code anything special to define the main procedure; it consists of everything before the first Procedure specification.
A sub procedure is kind of like a subroutine but you give it a name and then you can use like a %BIF.
Here is a very simple example:
Imagine you wrote a sub-procedure that received a parameter of ‘customer number’ and it went and found the ‘customer email address’ and you decide to call this procedure ‘GetCustomerEmail’ then you would use it in your RPG like this:
namefield = GetCustomerEmail( CusNum );
and that would execute the procedure and return the name into the variable namefield.
now you could define that subprocedure in your program – right at the bottom after the PSSR subroutine.
Here is a slightly more complex example:
How to use a sub-procedure to easily do same thing in lots of programs. Lets say I have the need to check if I am running in Batch or Interactive?
I had this problem ages ago, so I added a sub-procedure to my main service program that would return a YES or NO when asked ‘am i running interactive’
You could code it in your RPG like this:
IsThisJobInteractive = #Interactive();
The code for this sub-procedure looks like this:
// +-------------------------------------------------------------------------------------+ // | #interactive - purpose : check if job is running interactive | // | input : none | // | Returns : true or false (1/0) | // +-------------------------------------------------------------------------------------+ /if defined(#Interactive) P #Interactive B Export //*check if job is running interactive d #Interactive PI 1N //*retrieve job information (qusrjobi) api dRetrieveJobInfo... d PR Extpgm('QUSRJOBI') d @Receive Like(JobStatus) d @JobLength 10I 0 d @JobFormat 8A d @JobName 26A Const d @JobIndent 15A Const d @ErrCode Like(apiError) //data definition section //*procedure work variables dInteractive S N dJobLength S 10I 0 Inz(%Size(JobStatus)) dJobFormat S 8A Inz('JOBI0100') //*job status data-structure dJobStatus DS d JobBytesRtn 10I 0 Overlay(JobStatus:*Next) d JobBytesAvl 10I 0 Overlay(JobStatus:*Next) d JobName 10A Overlay(JobStatus:*Next) d JobUser 10A Overlay(JobStatus:*Next) d JobNumber 6A Overlay(JobStatus:*Next) d JobIdentifier 16A Overlay(JobStatus:*Next) d JobStatusCode 10A Overlay(JobStatus:*Next) d JobType 1A Overlay(JobStatus:*Next) d JobSubtype 1A Overlay(JobStatus:*Next) d JobReserv1 2A Overlay(JobStatus:*Next) d JobRunPriority 10I 0 Overlay(JobStatus:*Next) d JobTimeSlice 10I 0 Overlay(JobStatus:*Next) d JobDefaultWait 10I 0 Overlay(JobStatus:*Next) d JobPurge 10A Overlay(JobStatus:*Next) /free // Use API to retrieve job running status RetrieveJobInfo(JobStatus:JobLength:JobFormat: '*':' ':apiError); If JobType = 'I'; Interactive = *ON; Else; Interactive = *OFF; EndIf; Return Interactive; /end-free P E /endif
Now we wouldnt want to add this bunch of code to everything we wrote that wanted to use it would we?
In the olden days we might have done it in a /COPYBOOK but this requires re-compilation of everything if we change the copybook. Thats smelly.
Welcome to the world of Service Programs
A service program is basically a little RPG thing you define all of you sub-procedures in. Sort of looks like an RPG program that just contains lots and lots of subroutines… but they are not subroutines they are sub-procedures. You can tell its a service program because the Hspec will say ‘NOMAIN’ and this literally means it has no MAINLINE CODE it’s just a list of sub-procedures.
Obviously, if you are using a service program in your RPG program you need to tell your RPG program to go and use that *SRVPGM to get its subprocedure list. You can either do that by adding it to your compile parm:
CRTPGM PGM('myprogram') BNDSRVPGM((TOBYSRVPGM))
or be much neater and use a Binding Directory.
Then just use your H spec:
In this example I will show you how I use the binding directory I created for Projex4i.
A binding directory is thing that has a list of service programs in it. you can add lots of them. you would use WRKBNDIRE to look at all the entries in a binding directory:
So, here you can see I have 4 service programs in my Projex4i binding directory.
Each of those service programs can have lots of sub-procedures in it. Or just one. It’s your call.
Look at SRVPGM(@SRVCORE) for example:
DSPSRVPGM SRVPGM(@SRVCORE) Service program . . . . . . . . . . . . : @SRVCORE Library . . . . . . . . . . . . . . . : PROJEX4I Owner . . . . . . . . . . . . . . . . . : NLITTEN Service program attribute . . . . . . . : RPGLE Detail . . . . . . . . . . . . . . . . . : *PROCEXP #CAPITALISE #CENTER #CHECKCOMMAND #CLEANCHEVRON #CLEANIFSDOCUMENT #CLEANIFSFOLDER #CLEANIFSURL #CLEANOBJECTNAME #CLEANWHACK #DISPLAY #DUMPCALLSTACK #FILEFMTNAME #FILEINFO #FINDCALLER #GENRANDOM #GETIBMILPAR #GETIBMISERIAL #GETIBMISYSNAME #COMMANDLINE #GETIBMIVERSION #GETPROJEXDAYS #GETPROJEXKEY #GETPROJEXSTATUS #GETSYSTEMVALUE #INTERACTIVE #LEANBACKWARD #LEANFORWARD #LOWERCASE #MBRTEXT #QUOTE #REGISTERPROJEX #RUNCOMMAND #SCANREPLACE #SEARCHINDEX #SPLITSTRING #SQUISH #SQUISHSINGLE #UNDERSCOREIT #UPPERCASE #VALID8EMAIL
For example – Note that this service program contains a sub-procedure called ‘#Interactive’ it could be called anything I just like to use #something so I know its one of my sub-procedures. Some people argue that using special characters is un-necessary noise. But I am not a number i am a free man!
So, in any program that defines this binding directory in its H-SPEC will have automatic access to all those sub-procedures from its RPG (or incidentally CL commands).
So, whenever you have repeatable code – you define it as a subprocedure then you can use it everywhere.
Procedures are as slippery as a sausage I’m sure you will agree.