• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • A Style Guide for Modern RPG and ILE, Part 2

    October 18, 2016 Paul Tuohy

    One of the basic principles of programming is that coding conventions (guidelines and standards) improve the readability of source code and make software maintenance easier. Coding conventions provide the foundation for developing applications that are easy to maintain and modify. This article completes the style guide to coding RPG programs using free-form RPG in an ILE environment started in A Style Guide For Modern RPG And ILE, Part 1.

    Older Functions

    When using free-form RPG (and nothing but free-form RPG), a lot of the old RPG “functionality” is no longer available (e.g., operation codes such as MOVE, MOVEL, GOTO, ADD, SUB, condi-tioning and resulting indicators). But there are still some old features that are available but should be avoided.

    RPG’s Built-In Indicators

    The use of RPG’s numeric indicators (*IN01 to *IN99) and special indicator (*INU1 to *INU8) should be avoided at all costs. They are not self-explanatory and are dependent on comments to clarify their usage.

    Define your own indicators when a Boolean condition is required. Better to have something like this:

    if monthEnd;
    

    As opposed to this:

    if *in70;
    

    If the program has externally described display files or print files, which make use of numeric conditioning and/or resulting indicators, then define an indicator data structure to remap the numeric indicators in the display or print file to indicators with meaningful names in the program.

    dcl-F Mod30101D workstn(*ext) usage(*input:*output) IndDs(WSI);
    dcl-Ds WSI qualified;  
       F3Exit       ind pos(3);
       F5Refresh    ind pos(5);
       F12Cancel    ind pos(12);
       F23Delete    ind pos(23);
       pageDown     ind pos(26);
       pageUp       ind pos(27);
    
       errorInds    char(10) pos(31);
       enableDelete ind pos(41);
    
       SFLInds      char(3) pos(51);
       SFLDsp       ind pos(51);
       SFLDspCtl    ind pos(52);
       SFLClr       ind pos(53);
    
       SFLNxtChg    ind pos(54);
       SFLPageDown  ind pos(55);
       SFLPageUp    ind pos(56);
       SFLProtect   ind pos(57);
       enableMsgSFL ind pos(91) inz(*on);
    end-Ds;              
    

    Compile Time Arrays

    The definition of a compile time array means that the definition of the array (in the data declarations), is separate from the definition of the data (which is at the end of the program).

           dcl-S monthNames char(9) dim(12) ctData perRcd(3);
             // (lots and lots and lots of code here)
    **CTDATA MonthNames
    January  February March
    April    May      June
    July     August   September
    October  November December
    

    It is better to define an array and its corresponding data in a data structure–the definition of the data structure overlaying the definition of the data.

    dcl-Ds allDays;
       *N         char(9) inz('January');
       *N         char(9) inz('February');
       *N         char(9) inz('March');
       *N         char(9) inz('April'); 
       *N         char(9) inz('May');
       *N         char(9) inz('June');
       *N         char(9) inz('July');
       *N         char(9) inz('August');
       *N         char(9) inz('September');
       *N         char(9) inz('October');
       *N         char(9) inz('November');
       *N         char(9) inz('December');
       monthNames char(9) dim(12) pos(1);
    end-Ds; 
    

    The definition of an array in the same location as the data makes it easier to modularize code into subprocedures when required.

    Multiple Occurrence Data Structures

    Data structure arrays should be used in place of multiple occurrence data structures. Multiple occurrence data structures only allow access to one occurrence (element) at a time (e.g., cannot directly compare two occurrences of a multiple occurrence data structure) and setting the required occurrence (OCCUR/%OCCURS() ) is cumbersome, as opposed to simply using an index to identify an array element.

    Embedded SQL

    Apart from the standard style guidelines, there are a few guidelines that relate specifically to embedded SQL.

    • Keep SQL statements as simple as possible. You do not want to have to debug a complex SQL statement in a RPG program. Create views that “hide” the complexity of joins and casting and se-lect from the view in the RPG program.
    • Avoid naming variables that start with SQ, RDI, or DSN. They might conflict with variable names that are automatically included by the SQL pre-compiler.
    • Use SET OPTIONS to ensure that the SQL environment is specified correctly at compile time.
    • Wherever possible, make use of multi-row fetch as opposed to single-row fetch.

    Global Definitions

    As mentioned earlier, global definitions should be kept to a minimum.

    The most valid candidates for global definitions are file and/or data area definitions. If they are being used, they should be modularized within a module with the subprocedures that will be processing them. Consider using qualified data structures for all I/O.

    If global variables are being defined, they should be qualified (either in a data structure or with a prefix such as g_) so they are easily identified as global variables within a subprocedure.

    The use of the IMPORT and EXPORT keywords for variables should only be used as a last resort and should be well documented when used.

    Parameters, Prototyping, and Procedure Interfaces

    As well as providing a means of validating parameter definition at compile time, prototyping and procedure interfaces allow you to specify how parameters are used.

    Remember, prototypes are only required for external subprocedure or program calls. They are not required for subprocedures that are coded and only called within the same module/program.

    Parameters

    If the value of a parameter is not changed by a subprocedure/program, use the VALUE or CONST keywords to indicate that such is the case. These stop parameters from being inadvertently changed by a subprocedure or program.

    Return Value

    Subprocedures can be used to code procedures, which do not return a value, and functions, which do return a value. (A call to a program would be considered a procedure call, since programs cannot return a value.) One of the items that is often debated is whether or not subprocedures should always return a value.

    The argument for always returning a value is that there is always a value to return. For example, even if a procedure does not calculate and return a specific value, should it return a value to indicate whether or not the process worked?

    As a preferred practice, don’t force procedures to return a value.

    Copy Members

    All prototypes should be coded in copy members and included in required programs and modules. A prototype should never be coded in more than one source member.

    The copy members that contain the definition of the prototypes should also contain the definition of any templates and named constants that refer specifically to the use of the corresponding prototypes.

    Managing the maintenance and usage of the prototype members can be quite a challenge and, at the moment, there is no single simple solution. Every solution comes with a cost.

    The prototype members must be easy to maintain. You do not want a situation where two programmers need to change prototype definitions in the same member at the same time. This means that there will usually be a one-to-one correspondence between prototype members and modules, i.e., a prototype member containing all prototypes, templates, and named constants for subprocedures in a corresponding module. The same one-to-one correspondence would apply to prototypes for program calls although a “program” prototype member might contain prototypes for a group of related programs.

    The ease of maintenance requirement means there will be quite a few prototype members. There is now the challenge of how the programmer knows which members to include in a program when a call is to be made to a program or an external subprocedure. This is even more challenging if the prototype members are in a source physical file with a 10-character naming restriction, there is little chance of having meaningful names. An alternative is to define prototype members in directories in the Integrated File System (IFS), where meaningful names can be given to the prototype members.

    Compare the following directive to include a prototype member from a source file:

      /include qrpglesrc,UTILITY01P
    

    With the corresponding directive for a file in the IFS:

      /include '/myApp/proto_utility/userSpaceAPIs.rpgle'
    

    The requirement for /INCLUDE directives can be reduced by using nested copies. It is possible to have a single /INCLUDE directive that would include all prototype members in the application. Whereas this approach is appealing (the programmer only needs to know the name of one copy member), there are a number of considerations:

    • Someone must be responsible for maintaining the extra copy members that handle the nesting and grouping of the prototype members.
    • All prototypes, templates, and named constants (defined in prototype members) will now be included in the Outline View in RDi. If there are a lot of prototypes, templates and named constants–the Outline View will take a long time to refresh. This can be somewhat alleviated through the use of conditional compilation directives (as you will see in a following example) but that, in turn, leads to even more complicated maintenance of the extra copy members that handle the nesting and grouping of the prototype members.
    • Change management systems may have difficulty when a change is made to prototype member (even as simple a change as adding a comment). The change management system may deter-mine that, since the member is included in every program and the member has changed then every program should be recreated.

    The following example shows one approach to using nested copies to minimize the requirement for multiple /INCLUDE directives. A program contains the following /INCLUDE directive:

     /include common,baseInfo
    

    The BASEINFO member contains nested include directives, as follows:

     /include utility,pUtility
     /include fileProcs,protoFile
     /include common,commproto
     /include genstat,pStatGen
     /include regFunc,pRegFunc
      //--------------------------------------------------------
      // Include CGI Prototypes, if required
     /If Defined(CGIPGM)
     /include CGIDEV2/QRPGLESRC,PrototypeB
     /include CGIDEV2/QRPGLESRC,Usec
     /EndIf
      //--------------------------------------------------------
      // Include HSSF Prototypes, if required
     /If Defined(HSSF)
     /include SIDSrc,HSSF_H
     /EndIf
    

    Each of the included members might contain prototype definitions or might contain further nested include directives. For example, the PUTILITY member, in turn, contains nested include directives, as follows:

      /include utility,PUTILMSGS
      /include utility,PUTILCGI
      /include utility,PUTILSPACE
      /include utility,PUTILIFS
      /include utility,PUTILDATE
    

    The members PUTILMSGS, PUTILCGI, PUTILSPACE, PUTILIFS, and PUTILDATE contain the actual prototypes.

    We can alleviate the overhead on the refresh of RDi’s Outline view by using compiler directives to indicate what should and should not be included. Changing the definition of the PUTILITY member as follows means that a definition name must be set in order for the prototypes to be included.

      /if defined(UTILITY)
      /define UTIL_MESSAGE
      /define UTIL_CGI
      /define UTIL_USERSPACE
      /define UTIL_IFS
      /define UTIL_DATES
      /endIf
    
      /if defined(UTIL_MESSAGE)
      /include utility,PUTILMSGS
      /endIf
      /if defined(UTIL_CGI)
      /include utility,PUTILCGI
      /endIf
      /if defined(UTIL_USERSPACE)
      /include utility,PUTILSPACE
      /endIf
      /if defined(UTIL_IFS)
      /include utility,PUTILIFS
      /endIf
      /if defined(UTIL_DATES)
      /include utility,PUTILDATE
      /endIf
    

    The originating program (that includes the member BASEINFO) can set the required definition names or can define UTILITY, which would result in all members being included.

     /define UTIL_MESSAGE
     /define UTIL_USERSPACE
     /include common,baseInfo
    

    The “difficulty” with this approach is that the programmer must know the required definition names–so documentation is required.

    The benefit of this approach is that it documents what is being included in the program.

    Note that in this example, definition names are being used to include single members, but they could just as easily be used to include multiple members (as with CGIPGM in the BASEINFO member).

    As stated earlier, there is no simple solution to managing the maintenance and usage of the prototype members. The challenge is to find which combination of techniques will provide the best balance between ease of maintenance and ease of use.

    The Integrated Language Environment

    There are many approaches to setting standards for the Integrated Language Environment (ILE). Common questions are:

    • How many service programs should you have?
    • How many modules should there be in a service program?
    • How many modules should there be in a program?
    • When should you use bind by copy and bind by reference?
    • How many binding directories should you have?
    • How should you control signature violations?
    • How many activation groups should you have?

    Unfortunately, the answer to all of these questions is: It depends. The structure of an ILE application depends on the application and what it does.

    One example of how to structure an ILE application can be found in my article Development Environments.

    A change management system is something that can have a major impact on the methodology you use when developing an ILE application in that the change management system may have a preferred technique for managing service programs, binder language, etc.

    Regardless of the final development environment, these are some of the dos and don’ts for an ILE environment.

    ILE Programs

    Although the structure of ILE programs can be more complicated (one or more modules, binding to service programs), you still want the process of creating a program to be a simple one. In order to compile a program, the programmer should not need to know about activation groups and what all the service programs are, let alone what subprocedures are in what service programs.

    This can be achieved through the diligent use of binding directories and a standard control specification, which gets included in every program via an /INCLUDE directive.

    Ctl-Opt debug datEdit(*MDY/) option(*srcStmt:*noDebugIO) bndDir('MYAPP');
    /if defined(*CRTBNDRPG)
    Ctl-Opt dftActGrp(*no) actGrp('MYACTGRP');
    /endIf 
    

    Service Programs

    Service programs are at the core of any ILE application. If a subprocedure can be used in more than one place, then it belongs in a service program.

    Since content of a service program can be called from multiple places, the management of a service program requires a bit more care than “normal” programs. The approach to managing changes to a service program should be along the same lines as the way changes to a database are managed. As with a database, the minimum of people should have the ability to make changes. Any programmer can develop a subprocedure, but not any programmer can incorporate it in a service program.

    Here are some basic pointers for service programs:

    • How many service programs should an application have? There is no magic number. Each service program should contain groups of related functions: utilities, database processing, API handlers, etc. For ease of maintenance, some service programs might be split into multiples (e.g., database processing).
    • As with service programs themselves, the number of modules per service program and the number of subprocedures per module should be determined by related functions and ease of use. Ease of maintenance is the key.
    • Keep the source members for a service program separate from other source members. Maybe use a source file name that has the same name as the program.
    • Determine how you will manage prototype/template members for the contents of the service program (one copy member, a copy member per module etc.).
    • Consider having a separate binding directory for each service program. The bindings required for a service program can be different from the bindings required for an application program.
    • Always use binder language. Never use EXPORT(*ALL) when creating a service program. Binder language does mean more maintenance (when subprocedures are added/removed), but it also allows the greatest flexibility in managing the interface to the service program.
    • When adding new parameters to a subprocedure, add the parameters to the end of the parame-ter list using OPTIONS(*NOPASS). This minimizes the requirement of re-creating anything that calls the changed subprocedure.

    Binding Directories

    When creating an ILE program or service program, binding directories provide a means of generically providing a list of modules and service programs that may be required for binding.

    Binding directories should be kept to a minimum. A single binding directory should list all service programs that might be required when a standard program is created.

    Depending on the complexity and cross referencing between service programs, each service program might also require a binding directory. If there are multiple ILE applications and there are service programs and modules that are common to all, then another binding directory may be used to list those objects.

    Remember, a list of binding directories can be included in the BNDDIR keyword on an RPG control spec.

    There have been cases where an application has one binding directory per program–the binding directory lists the modules and service programs required to define the program. This is a mistake and imposes a maintenance overhead that is not required.

    Managing service programs with many modules has to be done either by having a specific binding directory for the service program, by writing a CL program to create the service program, or by using a generic name for all modules in the service program.

    Activation Groups

    Generally speaking, an activation group applies to an application. At times, a separate activation group might be used if scoping for commitment control and/or file overrides.

    ILE programs should never be run in the default activation group. This can only be achieved if an ILE program is created with an activation group of *CALLER and the program is called by an OPM program (or from a command line). It is best to use the name of the activation group when creating programs. This is easily achieved using the ACTGRP keyword in a standard Control Spec in the programs.

    Even worse than having an ILE program in the default activation group is having a service program in the default activation group. This can only happen if an ILE program is running in the default activation group. Again, ILE programs should never be run in the default activation group.

    Also, avoid using the QILE activation group. This is the activation group that is used when care has not been taken to choose the activation group. If care was not taken to choose the activation group, care may not have been taken in other aspects of the application, and your application may be subject to the activation group ending unexpectedly, or to overrides and commitment control actions that you do not want.

    Roll Your Own

    Hopefully, these guidelines will provide a starting point for setting your own guidelines and standards. Another excellent source for style guidelines may be found in Appendix D of “Pro-gramming in RPG, 5th Edition” by Jim Buck and Bryan Meyers. Please let me know if you have any comments or additions.

    Paul Tuohy is CEO of ComCon, an iSeries consulting company, and is one of the co-founders of System i Developer, which hosts the RPG & DB2 Summit conferences. He is an award-winning speaker who also speaks regularly at COMMON conferences, and is the author of “Re-engineering RPG Legacy Applications,” “The Programmers Guide to iSeries Navigator,” and the self-study course called “iSeries Navigator for Programmers.” Send your questions or comments for Paul to Ted Holt via the IT Jungle Contact page.

    RELATED STORIES

    A Style Guide For Modern RPG And ILE, Part 1

    Development Environments

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags:

    Sponsored by
    DRV Tech

    Get More Out of Your IBM i

    With soaring costs, operational data is more critical than ever. IBM shops need faster, easier ways to distribute IBM applications-based data to users more efficiently, no matter where they are.

    The Problem:

    For Users, IBM Data Can Be Difficult to Get To

    IBM Applications generate reports as spooled files, originally designed to be printed. Often those reports are packed together with so much data it makes them difficult to read. Add to that hardcopy is a pain to distribute. User-friendly formats like Excel and PDF are better, offering sorting, searching, and easy portability but getting IBM reports into these formats can be tricky without the right tools.

    The Solution:

    IBM i Reports can easily be converted to easy to read and share formats like Excel and PDF and Delivered by Email

    Converting IBM i, iSeries, and AS400 reports into Excel and PDF is now a lot easier with SpoolFlex software by DRV Tech.  If you or your users are still doing this manually, think how much time is wasted dragging and reformatting to make a report readable. How much time would be saved if they were automatically formatted correctly and delivered to one or multiple recipients.

    SpoolFlex converts spooled files to Excel and PDF, automatically emailing them, and saving copies to network shared folders. SpoolFlex converts complex reports to Excel, removing unwanted headers, splitting large reports out for individual recipients, and delivering to users whether they are at the office or working from home.

    Watch our 2-minute video and see DRV’s powerful SpoolFlex software can solve your file conversion challenges.

    Watch Video

    DRV Tech

    www.drvtech.com

    866.378.3366

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Sponsored Links

    Chrono-Logic:  Deploy automatically to multiple IBM i and Windows servers with a single click!!
    Fresche:  IBM i staffing for all of your IT needs. Request a FREE estimate. 1-800-361-6782
    Manta Technologies Inc.:  The Leader in IBM i Education! Download catalog and take sample sessions!

    Mobile Apps As Easy As RPG III On Your IBM i Radar Now: GDPR

    Leave a Reply Cancel reply

Volume 16, Number 23 -- October 18, 2016
THIS ISSUE SPONSORED BY:

T.L. Ashford
WorksRight Software
Focal Point Solutions Group

Table of Contents

  • A Style Guide for Modern RPG and ILE, Part 2
  • Replacing Source in The Twenty-First Century
  • SQL PL Conditional Structures

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • Meet The Next Gen Of IBMers Helping To Build IBM i
  • Looks Like IBM Is Building A Linux-Like PASE For IBM i After All
  • Will Independent IBM i Clouds Survive PowerVS?
  • Now, IBM Is Jacking Up Hardware Maintenance Prices
  • IBM i PTF Guide, Volume 27, Number 24
  • Big Blue Raises IBM i License Transfer Fees, Other Prices
  • Keep The IBM i Youth Movement Going With More Training, Better Tools
  • Remain Begins Migrating DevOps Tools To VS Code
  • IBM Readies LTO-10 Tape Drives And Libraries
  • IBM i PTF Guide, Volume 27, Number 23

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle