In RPG IV, compiler directives have undergone quite a few changes in the last few years. Most of you are familiar with the /COPY or /INCLUDE directive and even a few of us may remember the /TITLE, /EJECT and /SPACE directives. Then again, there are some who have never used a /COPY directive. In this article, I want to share the way I use compiler directives in my programs.
Compiler directives for formatting compile listings may have gone the way of the dodo but, as you progress towards ILE, you might be forgiven for thinking that there would be fewer places for compiler directives like the humble /COPY; since one of the main benefits of ILE is the calling of subprocedures, which negates the requirement for having standard code copied in multiple places.
Compiler directives have a key part to play in the development and maintenance of any well-written RPG IV program, especially those written for an ILE environment.
In this article, I want to share the way I use compiler directives in my programs.
As the name implies, a compiler directive is an instruction (directive) to the compiler to perform a specific task during compilation. A compiler directive is indicates by a '/' in position seven of a line followed by one of the directive statements.
For those of you using free-form RPG, /FREE and /END-FREE are compiler directives that tell the compiler how it should interpret the enclosed code.
By far the most common compiler directives are /COPY or /INCLUDE. Both of these directives perform the same basic function but are handled differently by the SQL pre-compiler depending on the RPG pre-processor options parameter (RPGPPOPT) specified on the CRTSQLRPGI command.
Along the way you will see some of the "new" compiler directives like /DEFINE and /IF DEFINED.
Using compiler directives
I use three standard copy members in all my programs. This is without fail, and they are the exact same statements in every program.
Figure one demonstrates three ways of using the three standard directives for the inclusion of a standard H spec (STDHSPEC), standard D Specs (STDDSPEC) and a standard PSSR subroutine (STDPSSR). Note the following:
1. The first example is for a standard 5250 or batch application. The standard specs are included.
2. The second example uses the DEFINE directive to indicate that CGI procedures are being used (you will see where this is used in a moment).
3. The third example uses the DEFINE directive to indicate that CGI procedures and calls to system APIS are being used. Additional H spec keywords that are specific to this source are also included.
(1) H/Copy QCpySrc,StdHSpec F**** F**** D/Copy QCpySrc,StdDSpec // Lots More Code /Copy StdPSSR (2) H/Define CGIPGM H/Copy QCpySrc,StdHSpec F**** F**** D/Copy QCpySrc,StdDSpec // Lots More Code /Copy StdPSSR (3) /Define UTILITY /Define APIS H/Copy QCpySrc,StdHSpec H NoMain F**** F**** D/Copy QCpySrc,StdDSpec // Lots More Code /Copy StdPSSR Figure 1: Including the three standard copy members
The copy members are stored in a separate source physical file (QCPYSRC) as opposed to the default of QRPGLESRC; I prefer to keep my copy members separate from my other sources. It is worth noting that copy members may also be referenced as Integrated File System (IFS) files as opposed to just source members.
These three simple directives ensure that my programs comply with consistent standards for my application and that every program will function correctly in the same environment. The only thing that a programmer needs to be aware of is the conditions that must be defined to ensure that the relevant bits and pieces are enclosed. Of course, you could just dispense with the definition of the conditions and include everything in every program, but I have found that these definitions act as a means of documenting the functionality of a program without the requirement for comments. For example, a source member that has a definition for CGIPGM indicates that this is a CGI program.
Let's have a look at each copy member.
Standard H Spec(1) H Debug DatEdit(*MDY/) Option(*SrcStmt:*NoDebugIO) CopyNest(10) (2) /If Defined(*CRTBNDRPG) /If Defined(CGIPGM) (3) H DftActGrp(*No) ActGrp('AGCGI') BndDir('ALLBNDDIR') /ElseIf Defined(UTILITY) H DftActGrp(*No) ActGrp('UTILITY') BndDir('ALLBNDDIR':'UTILITY') /Else H DftActGrp(*No) ActGrp(*Caller) BndDir('ALLBNDDIR') /EndIf /EndIf Figure 2 shows the definition of the standard H spec. Note the following:
1. The copy member starts with keywords that are common to all RPG source members regardless of their content or how they are compiled e.g. every RPG program should have Option(*SrcStmt:*NoDebugIO).
2. The /IF DEFINED(*CRTBNDRPG) directive determines if the source member is being compiled using the Create Bound RPG program (CRTBNDRPG) command: *CRTBNDRPG is a reserved condition name.
3. If compiling using CRTBNDRPG then include keywords for activation groups and binding directories (these keywords are invalid if compiling using the Create RPG Module (CRTRPGMOD) command). Different activation groups are specified depending on /DEFINE conditions previously specified.
(1) H Debug DatEdit(*MDY/) Option(*SrcStmt:*NoDebugIO) CopyNest(10) (2) /If Defined(*CRTBNDRPG) /If Defined(CGIPGM) (3) H DftActGrp(*No) ActGrp('AGCGI') BndDir('ALLBNDDIR') /ElseIf Defined(UTILITY) H DftActGrp(*No) ActGrp('UTILITY') BndDir('ALLBNDDIR':'UTILITY') /Else H DftActGrp(*No) ActGrp(*Caller) BndDir('ALLBNDDIR') /EndIf /EndIf Figure 2: Standard H Spec
When generating single module programs, this source member completely controls the ILE environment used. The programmer need not be concerned with the naming of activation groups or which binding directories to use.
Let's move on to the D Specs.
Standard D Spec
Modern RPG ILE programs make extensive use of prototypes and, more recently, standard structures and named constants. In the case of prototypes you will have often heard it stated that all prototypes should be defined in one copy member and included in every source using a copy directive: the same concept holds true for standard structures and named constants. While this is what you want to achieve it is often unmanageable since you may end up with a source member containing the definition of a couple of thousand prototypes. How can you get the best of both worlds by having source members of a manageable size but only have one copy directive?
Figure 3 shows the definition of the standard D spec. Note the following:
1. Copy members may contain copy or include directives for other members, which may, in turn, contain copy or include directives for other members and so on. You can even have recursive copies! The level of these nested copies may be controlled by the COPYNEST keyword on the H Spec (refer to Figure 2): which you should use if you are ever tempted to attempt recursive copies. The D Spec copy member starts with INCLUDE directives for copy members containing the definitions of base formats that are common to all programs. These copy members contain the definition of standard data structures, fields and named constants and are grouped together "logically". Figure 4 contains an example of some of the code included in one of these copy members.
2. Standard formats are followed by the inclusion of prototypes that are common to all programs. Again, the prototypes are grouped logically in source members.
3. Other copy members for base formats and/or prototypes are included depending on the prior definition of conditions.
(1) /INCLUDE QCPYSRC,FORMATBASE /INCLUDE QCPYSRC,FORMATMSGS (2) /INCLUDE QCPYSRC,PROTOFILE /INCLUDE QCPYSRC,PROTOMSGS //------------------------------------------ // Include API Prototypes, if required (3) /If Defined(APIS) /INCLUDE QCPYSRC,PROTOAPIS /EndIf //------------------------------------------ // Include CGI Prototypes, if required /If Defined(CGIPGM) /INCLUDE QCPYSRC,PROTOTYPEB /INCLUDE QCPYSRC,USEC /EndIf Figure 3: Standard D Spec
Like all programmers you want to know what one of these copy members looks like so Figure 4 shows you some of the contents of the FORMATMSGS copy member. Note the following:
1. The copy member contains the definition of a standard data structure that is based on a pointer. The value of the pointer is never set so this data structure will never be used in a program. Instead, programs will define data structures based on this definition using the LIKEDS keyword.
2. Named constants are used to provide meaningful names for message ids. The use of all uppercase for constant names is a convention used in many programming languages.
D Dummy_Ptr S * // Format in which error messages are stored. D Def_MsgFormat Ds Qualified D Based(Dummy_Ptr) D MsgId 7A D MsgText 80A D Severity 10I 0 D Help 500A D ForField 10A D ERR_NOTFOUND C 'ALL9001' D ERR_CHANGED C 'ALL9002' D ERR_DUPLICATE C 'ALL9003' D ERR_CONSTRAINT C 'ALL9004' D ERR_TRIGGER C 'ALL9005' D ERR_UNKNOWN C 'ALL9006' D VAL_DESCRIPTION... D C 'ALL0011' D VAL_INVALIDCATEGORY... D C 'ALL0012' D VAL_BUYEXCEEDSSELL... D C 'ALL0013' Figure 4: Some of the contents of the FORMATMSG copy member
There is just one more copy member to go.
Standard PSSR Routine
There is only one source member that I include in the calculation portion of a program and that is the PSSR subroutine. Refer to Exception/Error Handling in RPG and Exception/Error Handling in RPG Part 2 for further information on using this routine. Figure 5 shows the definition of the standard PSSR subroutine.
//---------------------------------------------------------- // *PSSR - Cancelation Routine // The Fatal program should perform an EndRqs, the use of // *INLR and Return is in case there is an error in the // calling of the fatal program. //---------------------------------------------------------- /Free BegSR *PSSR; DUMP; If Not *InLR; *INLR = *ON; FatalError(); EndIF; Return; EndSR; /End-Free Figure 5: Standard PSSR Routine
Although the quantity of compiler directives may have decreased the quality has certainly increased. I hope this article has illustrated how the use of compiler directives can make life easier when it comes to ensuring that a program has all the bits and pieces to make it work in a well designed application.
About the author: Paul Tuohy is CEO of ComCon, an iSeries consulting company. He is the author of "Re-engineering RPG Legacy Applications", "The Programmers Guide to iSeries Navigator" and the self teach course "iSeries Navigator for Programmers". He is also an award winning speaker who speaks regularly at US Common conferences and the renowned RPG World conferences.