Have Your Cake and Eat It, Too
April 18, 2012 Hey, Ted
I did not take your suggestion to write a subprocedure and embed it in a service program. It seemed like too much trouble to me. I wrote a program instead. It does the trick and it’s a lot less complicated than all those hoops you told me to jump through. I guess ILE isn’t all it’s cracked up to be.
Chuck presented me with a problem. I suggested he write a subprocedure to solve it. He didn’t like the idea of creating a module, writing binder language, creating a service program from the module, adding the service program to a binding directory, and referencing the binding directory when creating the calling program. He prefers the OPM model, which has none of that fancy stuff. Under OPM, a simple call is all you need to invoke a routine.
This situation reminds me of a dilemma I had when I was younger. I was in love with two girls: Kate and Edith. I asked my dad what I should do. He told me, “Son, you’re going to have to choose one of them. You can’t have your Kate and Edith, too.”
OK, corny jokes aside, I understand that there are times when calling a program is less trouble than binding to a subprocedure. In such cases, I recommend using the approach IBM takes with some APIs.
For instance, consider the Convert Case API. It has two implementations: subprocedure QlgConvertCase, and program QLGCNVCS. IBM pulls this off, not by writing two case-conversion routines, but by writing one case-conversion routine and providing two ways to access it. If you run the Display Program (DSPPGM) command against QLGCNVCS, you’ll see that it binds to service program QLGCASE, which exports subprocedure QlgConvertCase.
If you need to repeatedly call this routine, you should bind to subprocedure QlgConvertCase. But if a program only needs to call the routine once, you might prefer to call program QLGCNVCS. IBM gives you both options. You can have your cake and eat it, too!
Let’s go through the steps necessary to pull this off with your own routines.
First, create a module that contains the utility procedure. I’ll use the GetExtension routine that I published in the April 4, 2012, issue of Four Hundred Guru as an example. Here’s the module source code. I call it STREAMFILE because it’s a good start for a collection of stream-file routines.
H nomain /copy prototypes,StreamFile P GetExtension b export D pi 128a varying D inFileName 128a const *** locals D DotLoc s 10i 0 D Dot c const('.') D EmptyString c const('') D Extension s 128a varying /free monitor; if inFileName = *blanks; return EmptyString; endif; DotLoc = %scan('.': InFileName); if DotLoc <= *zero; return EmptyString; endif; Extension = %trim(%subst(inFileName: DotLoc + 1)); dow '1'; DotLoc = %scan('.': Extension); if DotLoc <= *zero; return Extension; endif; Extension = %trim(%subst(Extension: DotLoc + 1)); enddo; on-error; return EmptyString; endmon; return Extension; /end-free P e
Here’s the prototype for the subprocedure. RPG ILE callers will need it when binding to the service program.
D GetExtension pr 128a varying D inFileName 128a const
Create the module.
CRTRPGMOD MODULE(SOMELIB/STREAMFILE) + SRCFILE(SOMELIB/QRPGLESRC) SRCMBR(STREAMFILE)
Create a service program from the module.
CRTSRVPGM SRVPGM(SOMELIB/STREAMFILE) + MODULE(STREAMFILE) EXPORT(*ALL)
Now you can use the routine in ILE applications.
It’s time to create the second interface.
Write a wrapper program, GETEXTEN, to invoke the subprocedure.
*************** Wrapper program GETEXTEN D*** *entry plist D GetExten pr D inFileName 128a const D ouExtension 6a D GetExten pi D inFileName 128a const D ouExtension 6a D/copy prototypes,StreamFile /free *inlr = *on; ouExtension = GetExtension (inFileName); return;
Create the wrapper program.
CRTRPGMOD MODULE(SOMELIB/GETEXTEN) + SRCFILE(SOMELIB/QRPGLESRC) + SRCMBR(GETEXTEN) CRTPGM PGM(SOMELIB/GETEXTEN) MODULE(GETEXTEN) + BNDSRVPGM(STREAMFILE)
Now programs can call the “get extension” routine through a program interface, as this CL program does.
pgm dcl &StmfName *char 128 dcl &Extension *char 6 . . . omitted stuff . . . call pgm(GetExten) parm(&StmfName &Extension) . . . more omitted stuff . . .
You can’t always have it both ways in life, but this is one case when you can.