0% found this document useful (0 votes)
32 views

A Style Guide For Modern RPG and ILE, Part 2

This document provides guidelines for coding RPG programs using free-form RPG in an ILE environment. It discusses avoiding old RPG functions and indicators, defining your own indicators with meaningful names. It also recommends defining data structures to remap numeric indicators from display files to meaningful names. The document recommends defining arrays and data in data structures rather than using compile-time arrays. It suggests using array data structures instead of multiple occurrence data structures. Guidelines are also provided for embedded SQL, global definitions, parameters, prototyping, procedure interfaces, and managing copy members that contain prototypes.

Uploaded by

blackwolf
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views

A Style Guide For Modern RPG and ILE, Part 2

This document provides guidelines for coding RPG programs using free-form RPG in an ILE environment. It discusses avoiding old RPG functions and indicators, defining your own indicators with meaningful names. It also recommends defining data structures to remap numeric indicators from display files to meaningful names. The document recommends defining arrays and data in data structures rather than using compile-time arrays. It suggests using array data structures instead of multiple occurrence data structures. Guidelines are also provided for embedded SQL, global definitions, parameters, prototyping, procedure interfaces, and managing copy members that contain prototypes.

Uploaded by

blackwolf
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 15

THE FOUR HUNDRED SUBSCRIBE MEDIA KIT CONTRIBUTORS ABOUT US CONTACT

A Style Guide for Modern RPG and ILE, Part 2 Volume 16, Number 23 -- October 18, 2016

October 18, 2016 Paul Tuohy

THIS ISSUE SPONSORED BY:

T.L. Ashford
One of the basic principles of programming is that coding conventions (guidelines and
WorksRight Software
standards) improve the readability of source code and make software maintenance easier. Focal Point Solutions Group
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.
Table of Contents
dcl-F Mod30101D workstn(*ext) usage(*input:*output) IndDs(WSI); A Style Guide for Modern RPG and ILE, Part 2
dcl-Ds WSI qualified; Replacing Source in The Twenty-First Century
F3Exit ind pos(3); SQL PL Conditional Structures
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');
Content archive
monthNames char(9) dim(12) pos(1);
The Four Hundred
end-Ds;
Four Hundred Stuff
Four Hundred Guru
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

Tags:

Sponsored by
Briteskies

From RPG development, technical consulting services, and EDI solutions, to eCommerce
integration and security audits, Briteskies can handle it all. As a full-service consulting
company, we have IBM i experts in place to help you on a short-term project basis or long-
term assignment.

Read our blog for more information about our expertise. Check out our Customer Success
Stories to see how we help companies improve their efficiency.

Learn more at www.briteskies.com or Contact Us Now to see how we can help keep your
business running.

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

Recent Posts Subscribe Pages Search


AWS Inks Deal With Connectria To To get news from IT Jungle sent to About Us
Have a Power Play your inbox every week, subscribe Contact
IBM i Shops Have Alternatives to to our newsletter. Contributors
Db2 Web Query Four Hundred Monitor
Eradani Lays Waste to API Payload IBM i PTF Guide
Restrictions Media Kit
Four Hundred Monitor, November Subscribe
15
Old PHP and Other PASE Apps
Break on IBM i 7.5
New GM Wants To Push IBM
Power With Hybrid Cloud And AI
IBM Finally Comments On Db2
Web Query For i Withdrawal
IBM To Add Generative AI To
QRadar
IDC Boosts IT Spending Forecasts
For 2023 And Beyond
IBM i PTF Guide, Volume 25,
Number 46
Copyright © 2023 IT Jungle

You might also like