This article introduces the idea of an ILE module and explains how to use modules to create programs. In "ILE Basics: It's All About the Call" (March 27, 2008, article ID 56468 at SystemiNetwork.com), I emphasized that ILE is "all about the call." Specifically, it's about writing procedures -- small, reusable routines that you can call from any of the software that you write. In fact, that's what all the ILE concepts are about -- providing these reusable procedures. In "ILE Basics: Procedures" (May 22, 2008, article ID 56727), I described how to code procedures and explained the difference between main procedures and subprocedures. In this article, I take the next step and explain modules and programs. These are the disk objects that you use to deploy ILE modules on your server.
No doubt, you already know how to compile a program (*PGM) object. In ILE, program objects are not built directly from source code as they were in the Original Program Model (OPM) environment but instead are built from *MODULE objects.
Programs invoke a User Entry Procedure (UEP) when you call them. In other words, the "main line" of your program is invoked when you call your program. Even though your program might contain subroutines or subprocedures, those individual routines can be called only from within the same program. They can't be called from the command line or from other programs.
Service programs are just like program objects, except for one important difference: They lack a central routine (or UEP) to call. Instead, you call the subprocedures of the service program directly. Service programs provide a great way to write a routine once and call it from all over the place. I will cover service programs in depth in a future article.
Consider a simple RPG program, written in a traditional RPG style, similar to the way you might've written a program back in the RPG/400 days:
FPRICES IF E K DISK
D ItemNo s 7p 0
C *ENTRY PLIST
C PARM ItemNo
C PARM Price
C ItemNo chain Prices
c if %found
C eval Price = PrPric
c else
c eval Price = -1
c endif
C eval *INLR = *ON
Using ILE compile commands, if you want to create a program from the preceding source code, you type the following commands:
CRTRPGMOD MODULE(PRICEPGM) SRCFILE(*LIBL/QRPGLESRC) CRTPGM PGM(PRICEPGM) MODULE(PRICEPGM)
The first command, CRTRPGMOD, creates a module (*MODULE) disk object. In this case, the module is named PRICEPGM. A module object's only purpose is to be used to create a program or service program. Modules have no other use. You can't call a module directly. I tend to think of a module as a "half-waycreated program." In other words, I've started the process of compiling the program, but I haven't finished it.
The second command, CRTPGM, creates a program (*PGM) object from the module. To do that, it generates the various code required for all program objects, and then it copies the module contents into that program object. The result is an ordinary program that you can call with the CALL command, just like a program you might've created in the RPG/400 days with the CRTRPGPGM command.
Note that the module contents were copied into the program. That means that after the program object has been created, you no longer need the module. If you want, you can use the DLTMOD command to delete the module object, and the program still works. The reason it still works is because the module data has already been copied into the *PGM object, and therefore the *MODULE isn't used when the program is invoked.
The preceding example is for RPG -- but modules can be created from any ILE language. Here's a list of the commands that create modules:
The CRTPGM command is used to create the final program, no matter which language you wrote your module in. There's also the CRTSRVPGM command, which is very much like the CRTPGM command, except that it outputs a service program (*SRVPGM) object instead of a program (*PGM) object.
Seems silly, doesn't it? Why take two steps to create a program? What good does it do to create a module and then use it to create a program? Why not just create the program in the first place?
Well, actually, you can. In addition to the CRTxxxMOD commands, IBM provides CRTBNDxxx commands, which create a temporary module in the QTEMP library and then create a program from that module so that you can create programs with one command, just as you did in the OPM days. Here's a list of the CRTBNDxxx commands:
So why do we have this intermediate module object if all it does is create programs? The answer is that programs can (optionally) be created with more than one module. This ability is particularly useful in a situation in which you want to write a program in more than one language. Yes, you read that right -- you might want to write part of your program in CL, part of it in RPG, and possibly even part of it in C or Cobol. In ILE you can do that -- you can combine code from multiple programming languages to make a single program! Pretty neat, eh?
For example, let's say you have an RPG program that wants to know how many total records are in a physical file. That information is difficult to get in RPG. Sure, you can call an API to do it, but an API is a lot more work than simply calling CL's RTVMBRD command. Wouldn't it be nice if you could call RTVMBRD from RPG? You can! Well, you sort of can. You can write a program made up of mixed CL and RPG code by creating two modules, a CL module and an RPG module, and binding them together.
CL module GETRECS:
PGM PARM(&FILE &LIB &MBR &RECS)
DCL VAR(&FILE) TYPE(*CHAR) LEN(10)
DCL VAR(&LIB) TYPE(*CHAR) LEN(10)
DCL VAR(&MBR) TYPE(*CHAR) LEN(10)
DCL VAR(&RECS) TYPE(*DEC) LEN(10 0)
RTVMBRD FILE(&LIB/&FILE) +
MBR(&MBR) +
NBRCURRCD(&RECS)
ENDPGM
RPG module that uses GETRECS:
D GetRecs PR
D File 10a const
D Lib 10a const
D Mbr 10a const
D Recs 10p 0
D NbrRecs s 10p 0
D Rec s 10p 0
.
.
GetRecs( 'CUSTMAS'
: '*LIBL'
: '*FIRST'
: NbrRecs );
for Rec = 1 to NbrRecs;
read CUSTMAS;
msg = 'Processed ' + %char(rec) + ' of '
+ %char(NbrRecs) + ' records...';
endfor;
.
.
You compile this example as follows:
CRTCLMOD MODULE(GETRECS) SRCFILE(*LIBL/QCLSRC) CRTRPGMOD MODULE(PROCCUST) SRCFILE(*LIBL/QRPGLESRC) CRTPGM PGM(PROCCUST) MODULE(PROCCUST GETRECS)
The result of these compile commands is a single program object created from both the CL and the RPG modules. As before, the code from the module objects is copied into the program, so the executable program object contains both the CL and the RPG code inside the same program!
In the "ILE Basics: Procedures" article, I mentioned that two types of procedures exist: main procedures and subprocedures. I also mentioned that the main procedure is the part of the program called when the call command invokes your program. However, in this article, I say that a UEP is called when a program is invoked. Which statement is true?
The answer is that both statements are true. The important thing to understand is that you can create a program from multiple modules. Each module can have its own main procedure, so you can have more than one main procedure in your ILE program! In fact, the example I provide in the preceding section showed a program created from two modules, and both modules have a main procedure. There's no question that the CALL command is going to call one of the main procedures, but how does it know which one to call?
The CRTPGM command has a parameter called Entry Module (ENTMOD) that has a default value of *FIRST. With that in mind, note that the CL command that I invoked to create the program looks like this:
CRTPGM PGM(PROCCUST) MODULE(PROCCUST GETRECS) ENTMOD(*FIRST)
Because PROCCUST (my RPG module) is the first module listed, the CRTPGM command calls PROCCUST's main procedure as its entry module. The main procedure of that module is, therefore, the UEP. When you call the program, that UEP is the procedure that it invokes.
Although my example demonstrates having two modules written in different languages, nothing is stopping you from having multiple modules written in the same language. Here are my thoughts about why you might want to do that:
Having said that, I've found that I rarely create a program from more than one module, except when I want to create a program from more than one language. Don't get me wrong -- I do use multiple modules from the same language but only rarely, when the code starts to get very long and hard to manage.
In addition to calling the main procedures of other modules, you can call subprocedures from other modules, as long as those procedures are exported. Main procedures are always exported, but subprocedures might not be.
In RPG, you export a subprocedure from the module by coding the EXPORT keyword on the beginning P-spec for that subprocedure. For example:
FPRICES IF E K DISK
/copy PRICE_H
P GetPrice B EXPORT
D GetPrice PI 9p 2
D Itemno 7p 0 const
/free
chain ItemNo Prices;
if %found;
return Price;
else;
return -1;
endif;
/end-free
P E
The fact that I coded EXPORT on my P-spec means that calling my GetPrice() subprocedure from outside this module is possible. Any other module bound into the same program (or service program) can call this routine simply by calling its prototype. For that reason, I put the prototype into a copy book. Notice the /copy directive in the preceding code. Here's what that copy book looks like:
D GetPrice PR 9p 2
D Itemno 7p 0 const
It's simply the prototype needed to invoke the GetPrice() routine. If my module had other procedures also intended to be called from outside the module, I'd put the prototypes for those procedures inside the same copy book. Because the prototype is in a copy book, I can easily bring it into the modules that need to call it:
/copy PRICE_H
.
.
NewPrice = GetPrice(MyItemNo);
In ILE C and C++, subprocedures (aka "functions") are exported from a module by default. To tell the compiler not to export them, you should code the static keyword. For example, in the following code, proc1 gets exported from the module, but proc2 does not:
#include <stdio.h>
int proc1(const char *test) {
printf("proc1: %s\n", test);
return 1;
}
static int proc2(const char *test) {
printf("proc2: %s\n", test);
return 2;
}
When working in a language that supports subprocedures (such as RPG or C), I design my program so that there's only one module that contains a main procedure. All the remaining modules contain only subprocedures to be invoked (either directly or indirectly) by the main procedure. So I have one module that contains the main procedure, and that main procedure is in my entry module and is therefore my UEP. All the other modules contain small, independent subprocedures. My main routine calls these subprocedures as needed to get the work done.
However, I've already mentioned that I rarely use multiple modules. Where do I put all my subprocedures, then? The ones that I expect that only this particular program will use get coded in the same module as the UEP. The ones that I expect to be reused get placed in a service program. I try very hard to make my routines reusable, because it saves me time later. So I write a lot of service programs -- and I discuss that in the next installment of the ILE Basics series.
ILE Basics: It's all About the Call:
http://systeminetwork.com/article/ile-basics-its-all-about-call [2]
ILE Basics: Procedures:
http://systeminetwork.com/article/ile-basics-procedures [3]
Prototypes: Beyond Call/Parm
http://systeminetwork.com/article/prototypes-beyond-callparm [4]
Writing Reusable Service Programs
http://systeminetwork.com/article/writing-reusable-service-programs [5]
Links:
[1] http://systeminetwork.com/author/scott-klement
[2] http://systeminetwork.com/article/ile-basics-its-all-about-call
[3] http://systeminetwork.com/article/ile-basics-procedures
[4] http://systeminetwork.com/article/prototypes-beyond-callparm
[5] http://systeminetwork.com/article/writing-reusable-service-programs