Sei sulla pagina 1di 23

RPG IV - Did You Know ....... ?

OCEAN Technical Conference


Catch the Wave

Susan M. Gantner
Partner400
susan.gantner @ partner400.com
www.partner400.com Your partner in AS/400 and iSeries Education

(Prototypes, Compiler Directives and other features and functions you may have missed) As I travel around the
world talking to RPGers, I often find that even experienced RPG IV programmers have "missed" a few gems along
the way. This session is designed to fill those gaps. Examples? How about Prototypes? Most people know that you
need prototypes when defining and using Subprocedures, but many fail to appreciate their other uses. For
example, did you know that you can use prototypes to call PGM objects? or that a prototype will defend against
passing incorrect parameters? Or that in some cases they can take care of the differences for you? Did you know
that you can conditionally include source lines based on a parameter for the RPG compiler? A great way to
include/exclude additional code needed for testing without having to delete or comment it all out. There are many
other uses for this great feature too. How about mail-merge function? Did you know that through the combination
of two of the latest features of RPG IV that you now have an easy way of blending customer names and other
details into text templates? Additional tidbits include new performance optimization options and effective use of
pointers in RPG trigger programs. Even if you have been programming in RPG IV for some time, you will likely
learn something new in this session!

The author, Susan Gantner, is co-founder of Partner400, a firm specializing in customized education and
mentoring services for AS/400 and iSeries developers. After a 15 year career with IBM, including several years at
the Rochester and Toronto laboratories, Susan is now devoted to educating developers on techniques and
technologies to extend and modernize their applications and development environments. This is done via on-site
custom classes as well as conferences and user group events.

Together with her partner, Jon Paris, Susan authors regular technical articles for the IBM publication, eServer
Magazine, iSeries edition, and the companion electronic newsletter, iSeries Extra. You may view articles in current
and past issues and/or subscribe to the free newsletter or the magazine at: http://eservercomputing.com/iseries/.

Feel free to contact the author at: susan.gantner @ partner400.com and visit the Partner400 web site at
www.partner400.com.

© Copyright Partner400, 2002. Did You Know ...... Page 1-2


Did you know that ......
You can embed compiler options in your RPG IV source?
Your can match statement numbers to SEU line numbers?
And you don't have to keep pressing F10 when debugging I/O
operations

RPG IV has many new string handling options?


Including BIFs for Mail-Merge type operations

Programs can be optimized based on actual usage


Prototypes can help you defend against parameter errors?
And allow RPG IV programs to directly use C functions?

Pointers are the way to handle trigger buffers


And the same techniques apply to many APIs

The compiler can selectively include/exclude source lines?

We will be covering a lot of ground in this session, from prototyping C functions to Triggers to.... well
a whole bunch of stuff. Some of it will (hopefully) be new to you and some may be familiar territory.

If you have questions or comments about this presentation, please feel free to contact the authors at
the e-mail address on the front cover.

This presentation may contain small code examples that are furnished as simple examples to
provide an illustration. These examples have not been thoroughly tested under all conditions. We
therefore, cannot guarantee or imply reliability, serviceability, or function of these programs.

All code examples contained herein are provided to you "as is". THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
DISCLAIMED.

All of which is the boring legal way of saying that any outbreak of plague, pestilence, flood or other
disasters apparently resulting from the use of these techniques is completely coincidental !!

© Copyright Partner400, 2002. Did You Know ...... Page 3-4


Compiler Options on H-spec
Embedded Compiler Options
Many options for CRTBNDRPG and CRTRPGMOD can now be specified
on the H spec as keywords.
The compile options specified will override the ones specified on the
CRTxxxxxx command.
The Compiler will search for the H spec in the following order:
An H spec included in your source
A data area named RPGLEHSPEC in *LIBL
A data area named DFTLEHSPEC in QRPGLE
The search stops when the first of these is found.
Use a /COPY source for any standard H-specs not a Data Area
Unsupported keywords:
DBGVIEW, OUTPUT, REPLACE, DEFINE, PGM, SRCFILE, SRCMBR,
TGTRLS

H DATFMT(*USA) TIMFMT(*USA) DFTACTGRP(*NO)


H COPYRIGHT('Release 5, Fix Level 27, Last Update 06/22/2001')

The Compiler will search for H specs in the following order:

An H spec included in your source


A data area named RPGLEHSPEC in *LIBL
A data area named DFTLEHSPEC in QRPGLE

The search stops when the first of these is found. Therefore, if you have specified H specs in the
data areas AND in your program, the specifications in the data area will NOT be used.

If you currently use one of the data areas to supply default H spec parameters, we strongly suggest
that you code them instead in a /COPY member and include this at the beginning of every source
member. Any additional parameters over and above the normal set can then be simply coded on H
specs following the /COPY directive.

© Copyright Partner400, 2002. Did You Know ...... Page 5-6


Debug Enhancements
Match program statement numbers with SEU !!
Use H spec keyword or command parameter OPTION(*SRCSTMT)
Easier for debugging and end user support

Faster step through I/O statements in debug sessions


User H spec keyword or command parameter
OPTION(*NODEBUGIO)
Skip the individual field steps for Input and Output specs
Faster step function during debugging

Specified on CRTRPGMOD & CRTBNDRPG commands


Or on the H spec
This last method is also available for releases prior to V4R4
Implemented via PTF

On the H spec, if you specify multiple options, you should separate the options with a colon (:).
For example: OPTIONS( *SRCSTMT : *NODEBUGIO)

With *SRCSTMT specified, the statement number reported when an error occurs during run time will
correspond directly to the SEU sequence number. Without this support, the statement number
reported did not correlate directly to the source statement numbers. Therefore, support of end user
problems was much more difficult. Many support desks kept compiler listings of all programs just to
be able to match the program statement numbers to SEU statement numbers.
*NOSRCSTMT indicates that line numbers are assigned sequentially.

If *SRCSTMT is specified, statement numbers for the listing are generated from the source ID and
SEU sequence numbers as follows: stmt_num = source_ID * 1000000 +
source_SEU_sequence_number

For example, the main source member has a source ID of 0. If the first line in the source file has sequence
number 000100, then the statement number for this specification would be 100. A line from a /COPY file
member with source ID 27 and source sequence number 000100 would have statement number 27000100.

Note: When OPTION(*SRCSTMT) is specified, all sequence numbers in the source files must contain valid numeric
values. If there are duplicate sequence numbers in the same source file, the behavior of the debugger may be
unpredictable and statement numbers for diagnostic messages or cross reference entries may not be meaningful.

If *DEBUGIO is specified, breakpoints are generated for all input and output specifications.
*NODEBUGIO indicates that no breakpoints are to be generated for these specifications. This means
that during debug sessions, doing a Step function on an IO statement required many steps (one for
each field in the format).

Note: These options were made available as H spec options only via a PTF on some previous releases of the RPG
compiler. NOTE: NEITHER of these new options is the default! Default behavior is as in prior releases.

© Copyright Partner400, 2002. Did You Know ...... Page 7-8


Editing BIFs (V3R7+)
Have the same effect as using an edit code or word on output
BUT - you can work with the resulting field!

%EDITC ( numexpr : editcode { : option } )


Returns the edited value of the numeric expression
option can be *ASTFILL, *CURSYM, or a character literal representing a
currency symbol

%EDITW ( numexpr : editword )


The edit word is applied to the value of the numeric expression.

D Text S 50

C Eval Text = 'Your balance of ' +


C %Trim(%EditC(Balance:'A':CurSym))
C + ' is now seriously overdue'

The same edit codes available for use on the O specs in RPG are available via the BIF.

© Copyright Partner400, 2002. Did You Know ...... Page 9-10


%CHAR (V4R2+)
%CHAR converts numeric, date, time fields to character
Allows for easier concatenation into strings

For numeric fields / expressions (V4R4+)


Provides a simple alternative to %EDITC when only simple editing is
required
Inserts decimal point and blanks leading zeros
In the example below if Balance was 275.00 and DueDate was
January 21st 2001 the field Text contains:
"Your balance of 275.00 is due on 01/21/2001"

D Text S 50
D DueDate S D DATFMT(*MDY)
D Balance S 9 2
C Eval Text = 'Your balance of ' +
C %Char(Balance)
C + ' is due on '
C + %CHAR(DueDate)

In V4R2, %CHAR was added but it only supported date, time and timestamp data types. In V4R4, it
was enhanced to work with numeric fields as well. This makes it much easier string together
numbers with character fields using the concatenate support.

Note that if you wanted to add the dollar sign to the Balance field, you could use the %EDITC BIF
discussed on the previous chart.

© Copyright Partner400, 2002. Did You Know ...... Page 11-12


Varying Length Fields
Variable length fields can provide a performance boost
When you append data to them the compiler knows just where to start
Guess which of these code samples runs faster !!

* Build content of InputFields into a comma delimited string


D OutString S 256a
C Eval OutString = %Trim(InputFields(1))
C For Count = 2 to 10
C Eval OutString = %TrimR(OutString) + ','
C + %Trim(InputFields(Count))
C EndFor

D OutString S 256a VARYING


C Eval OutString = %Trim(InputFields(1))
C For Count = 2 to 10
C Eval OutString = OutString + ','
C + %Trim(InputFields(Count))
C EndFor

Varying length fields make it easier and more efficient to concatenate character fields.

© Copyright Partner400, 2002. Did You Know ...... Page 13-14


"Mail Merge" with %REPLACE
%REPLACE makes string handling easier & more flexible.
When used in conjunction with other built-in functions, such as
%SCAN and %EDITC, this is particularly powerful
For example - code similar to the following could be used to substitute
the marker &S with the content of the variable Salutation in a
mail-merge type of operation.

D MsgText S 100A Varying


D BaseText C 'Dear &S, thank you for your +
D recent order'

C Read Customer
C If Not %EOF(Customer)
C Eval MsgText = %Replace( %TrimR(Salutation)
C : MsgText
C : %Scan('&S':MsgText)
C : 2 )
C EndIf

The syntax of the %Replace BIF is:

%REPLACE ( replacement : source { : start { : length } } )


start - defaults to 1 if not specified
length - defaults to the length of the string "replacement"
- if length is zero, string is simply inserted

© Copyright Partner400, 2002. Did You Know ...... Page 15-16


Performance Data Collection
Application profiling support
Collects statistical information on the way your code is actually run
during a specific span of time
The ILE optimizer can subsequently apply this information to more
specifically optimize the code to the way the users run it
e.g., re-order options on nested IF or SELECT statements (block order)
or re-order subprocedures based on usage (procedure order)

Steps to collect/apply performance data:


1. Modules must be enabled to collect performance data
2. Modules must have the profiling data attribute set on
Steps 1 & 2 can be done at compile time or later with CHGPGM/SRVPGM
3. Start the profiling data collection process
Subsequently end data collection
4. Apply the profiling data to the program(s)/service programs

To allow the program optimizer to more specifically optimize a module based on the way the users
actually use the program, you can enable application profiling. This can only be done on programs
that are Optimize(*Full) and Target Release(*Current). It can be useful in some situations,
particularly if you have many subprocedures or if you have large and complicated logic in the form of
statements such as IF/ELSE or SELECT. Based on the most popular options taken by the users
during the data collection process, the optimizer may actually choose to re-order some of the logic
blocks or re-order the subprocedures in the module to improve locality of reference.

Note that, of course, when this level of optimization is done, it can cause some interesting side
effects, especially during a source debug session if the code has been moved around in the
compiled code! Any modules with *FULL optimization can exhibit these kinds of side effects, but
those optimized with profiling data are more likely to exhibit them.

© Copyright Partner400, 2002. Did You Know ...... Page 17-18


Profiling Command Parameters
1. Enable performance collection (ENBPRFCOL) parameter
CRTRPGMOD, CRTBNDRPG, CHGPGM, CHGSRVPGM commands
Options are: *PEP, *ENTRYEXIT, *FULL

2. Set Profiling data attribute on (PRFDTA) parameter


CRTRPGMOD, CRTBNDRPG, CHGPGM, CHGSRVPGM commands
Options are: *NOCOL, *COL

3. Start profiling data collection process


STRPGMPRF command (no parameters)
Collects for ALL programs / service programs with PRFDTA(*COL)
ENDPGMPRF comand (no parameters) ends the collection process

Apply profiling data to program(s)


CHGPGM ... PRFDTA parameter
Options are: *APYBLKORD (block order), *APYPRCORD (procedure order),
*APYALL (both block and procedure order)

Note that profiling data can only be collected on modules where Optimize(*Full) and Target release
(*Current) have been selected.

ENBPFRCOL - This parameter tells the system to include the necessary information to allow this
module (or the modules in this program or service program) to be able to collect profiling data. It
does NOT ensure that profiling data will be collected when collection is started. To do that, you must
use the PRFDTA parameter. If you select *PEP here, statistics are kept only for entry and exit of the
PEP (program entry procedure) itself. Note that this does NOT include any RPG code -- not even
the main procedure. *ENTRYEXIT collects data on the entry and exit of all procedures in the
module (the main and any subprocedures). *FULL collects all the *ENTRYEXIT did, plus statistics
on calls to external procedures.

PRFDTA - This parameter causes data to be collected for this module (or the modules in this
program or service program) the next time profiling data collection is started. Changing this attribute
does not cause data to be collected immediately. After STRPGMPRF and ENDPGMPRF
commands have been run, this parameter on CHGPGM / CHGSRVPGM is used to apply the
profiling data to the modules for potential optimization.

STRPGMPRF - This is the command that actually begins the profiling data collection. Profiling data
will be collected for ALL programs and service programs on the system for which PRFDTA is
currently set to *COL. Note that the profiling data process puts extra workload on the system. It is
advised to collect profiling data for limited periods of time and for specific groups of programs at a
time.

© Copyright Partner400, 2002. Did You Know ...... Page 19-20


OS/400 Parameter Passing
When one program calls another, no data is passed
Only pointers containing the address(es) of the parameter(s)
These pointers reference the data back in the originating program
Which could be many levels back

In the example below, what does Part2 contain after the call ?
D TestData DS Program
D Part1 10 Inz('Test Data1') TEST1
D Part2 10 Inz('Test Data2')
C Call 'TEST2'
C Parm Part1

D Part1 S 15 Program
C *Entry PList TEST2
C Parm Part1
C Eval Part1 = *Blanks
C Return

These programs demonstrate the danger of mis-specifying a parameter in the called program.
This problem can also occur with numeric fields, but it is more likely to be obvious at the time of the
call.
With mis-sized character fields the effect may not be noticed until much later. The writer has encountered at
least one program where such an error went undetected for over 7 years!
If you have difficulty believing this, try compiling a running the two programs below.
* Program TEST1
D TestData DS
D Part1 10 Inz('Test Data1')
D Part2 10 Inz('Test Data2')
C 'In TEST1' Dsply
C TestData Dsply
C Call 'TEST2'
C Parm Part1
C 'In TEST1' Dsply
C TestData Dsply
C Eval *InLR = *On

* Program TEST2 - Demonstrates effect of mistreating parameters


D Part1 S 15
C *Entry PList
C Parm Part1
C 'In TEST2' Dsply
C Parm Dsply
C Eval Part1 = *Blanks
C Parm Dsply
C Return

© Copyright Partner400, 2002. Did You Know ...... Page 21-22


Prototyping Program Calls
Parameter mismatches can become a thing of the past!
The compiler will validate your program calls by checking:
The number of parameters
Their data type and size

Prototypes can accommodate differences in data definition


For example, allowing an integer to be passed when the callee expects
a value with decimal places

They can also allow a different name to be used on the call


Helps in documenting the function

Call is implemented by the new freeform op-code - CALLP


D TaxCalc PR ExtPgm('PX027C')
D GrossPay 8 2
D Allowances 8 2
C CallP TaxCalc( GrossPay : Allowances )

One of the really annoying problems with RPG III is that errors with parameter lists are not
discovered until run-time. It would be much better if we could have the compiler validate the
parameter lists for us.
Prototypes were added to the RPG IV language in the V3R2 and V3R6 releases. Although their
initial purpose was to support Prototyping of Subprocedures (more on these later) the RPG
developers realized that they could be used to provide this support. As you'll see they also provided
a number of other capabilities as well.

The type of error that can result from the incorrect use of parameters can often cause RPG
programmers sleepless nights and a difficult debugging tasks.
For example:
A 20 character field was expected but only a 10 character field was passed.
The called program is modified and now moves blanks to the field instead of the 10 character field it used to
move in. This results in a corruption of data in the calling program.
A character field is expected but a signed numeric is passed.
A situation like this can continue for years since the internal representation of a character "1" is identical to
the representation of the value 1 in a signed numeric field. A programmer makes a "simple change" and
passes a different numeric parameter. Suddenly the called program starts producing incorrect output. Why?
Because the new parameter while numeric is a packed field and the representation of the digit 1 is now completely
different causing the called program to fail to recognize it correctly.
In this section we will look at some of the powerful options that prototypes give us to deal with
situations such as these.
The important thing to note is that because the compiler is now able to validate parameters, errors of
the kind described above cannot occur

© Copyright Partner400, 2002. Did You Know ...... Page 23-24


Simple Prototype Example
If we are using these field definitions:

D GrossPay 8S 2
D Allowances 8S 2

Then the following CALL sequence:


C Call 'PX027C'
C Parm GrossPay
C Parm Allowances

Can be replaced by this Prototype and CALLP

D TaxCalc PR ExtPgm('PX027C')
D Gross 8S 2
D Allow 8S 2
C CallP TaxCalc( GrossPay : Allowances )

In our example the prototype has been hard coded in the calling program.
Normally we would expect to see it being /COPY'd in from a source file supplied by the programmer
who wrote PX027C. It is good practice to always produce a prototype when coding a new program.
Who knows better what parameters the program is expecting? For existing programs, code the
prototype when you next do maintenance.

While it may seem like more work at first glance, it is important to remember that one of our aims is
to reduce the opportunity for errors to be introduced during maintenance.
With a prototype in place, any change in the size and/or type parameters which in a conventional
CALL/PARM situation would produce an error, with CALLP will be detected by the compiler and will
therefore never get into production.

There are two main parts to a prototype


The first line is the PR line itself
This supplies the name of the program or procedure to be called
It also marks the beginning of the parameter list
The second and subsequent lines describe the parameters in sequence
The parameters are identified by a blank in positions 24-25 just as the subfields of a data structure are.
The parameter list is terminated by the appearance of any non-blank entry
For example a DS, S, C, PI (more on this later) or another PR

Note that the names used for the parameters do not match the names of the fields in the CALLP
In fact they could be completely blank and the compiler would be quite happy
It only cares about the number and type of parameters - the names used are irrelevant
Some people use a standard whereby the name used in the prototype identifies the type of field
e.g. Currency, Integer, Name, etc.

© Copyright Partner400, 2002. Did You Know ...... Page 25-26


Read-only Parameters
Specified by the keyword CONST
Implies that the called program will not modify the field
i.e. It will treat the parameter as Read-only
Allows the compiler to generate temporary fields when needed
For example if the size and/or type of the parameter don't match exactly
It also allows an expression to be used instead of a variable !

D TaxCalc PR ExtPgm('PX027C')
D GrossAmount 8S 2 CONST
D AllowanceAmount 8S 2 CONST
: : :
D Gross S 7P 2
D Base S 6P 2
D PersAllow S 5P 4
: : :
C CallP TaxCalc( Gross : Base + PersAllow )
C* Eval Temp1 = Gross
C* Eval Temp2 = Base + PersAllow
C* CallP TaxCalc( Temp1 : Temp2 )

The use of the CONST keyword allows the compiler to accommodate mismatches in the definition of
the parameters between the callee and the caller.
For example, the program we are calling expects a packed decimal value of five digits with no
decimal places, but the field we would like to use as a parameter is a three digit signed numeric.
Normally we would have to create a temporary variable (packed - five digits) and move the three
digit number to it. We would then pass the temporary field as the parameter. In fact the PARM
op-code provides this support though the use of Factor 2.

When you use the CONST keyword, you are telling the compiler that it is OK for it to make a copy of
the data prior to passing it to accommodate such mismatches. This avoids the need for us to
explicitly define a working variable of the correct size and type.

The compiler can accommodate differences in:


For Numeric fields: Size, decimal places, and type (zoned, packed, integer, etc.)
For Date fields: The date format
For Character fields: Length and type (fixed or varying)

Another benefit of using CONST is that it also allows an expression to be passed as a parameter.

© Copyright Partner400, 2002. Did You Know ...... Page 27-28


What Else Can Prototypes Do?
They are the key to many APIs shipped with the AS/400
The C function library
Which includes:
Random number generation
Math functions (Sin, Cos, Tan, etc.)
MI instructions (MI programming in RPG!)
Much more. . .
TCP/IP functions
You can use TCP/IP Sockets functions directly from RPG
ILE APIs
For Dynamic Memory, Error Handling and more
System APIs
Most new system APIs are written with the C programmer in mind
For example the Domino Hi-Test APIs

Prototypes can also be used to call procedures in other languages, most notably C. This means that
RPG IV programs now have access to all the functions in the C function library, which is shipped as
part of OS/400 on all systems. Other system APIs, previously available only to C programmers,
such as TCP/IP sockets and direct program access to the IFS, are also enabled by the prototyping
support associated with subprocedures.

Referring back to the QCMDEXC example, you might be interested to know that through the power
of prototyping the C "system" function is available. Some people prefer this to using QCMDEXC in
that it allows a more direct determination of the sucess/failure of a command. More on this later.

© Copyright Partner400, 2002. Did You Know ...... Page 29-30


Using Simple C Math Functions
Functions are available for Sine, Cosine, Tan, ArcTan, etc.
Here are sample prototypes for Sine and Cosine
Both return a double length floating point value
These are the C definitions
double sin(double x)
double cos(double x)
These translate in RPG IV to the following:

D Sine PR 8F ExtProc('sin')
D Double 8F Value
D Cosine PR 8F ExtProc('cos')
D Double 8F Value
* Once the prototypes are defined, the functions can be used just
* as if they were built into RPG IV. Like this:
C Eval SineX = Sine(X)
C Eval CosineY = Cosine(Y)

C math functions include acos, asin, atan, cos, cosh, sin, sinh, tan, tanh. All accept double floating
point parameters (8F in RPG IV) passed by value, and all return a double floating point value.

It may seem to you that these kinds of math functions are rarely used in RPG programs, and you are
right. However, on those occasions when they are needed then you will really appreciate the fact
that you don't have to try and "fake" the function out in RPG.

The new RPG Redbook (referenced later in this handout) includes an example of these C functions
in action.

© Copyright Partner400, 2002. Did You Know ...... Page 31-32


Is that all there is to it ?
There are two more things we need to do to use C functions
First we must specify that the program is a "real" ILE one
We do this by specifying DFTACTGRP(*NO)
Second we must bind the C library functions to our program
The easy way is to specify QC2LE as the Binding Directory
This saves us from having to worry about which Service Program(s) contain the
functions we need.

Embedding the parameters on the H-spec works well


You won't forget them that way !
You can also specify an Activation Group if you wish
Otherwise the default will be QILE

H BndDir('QC2LE') DftActGrp(*No)

C function calls are bound calls and can only be used from "real" ILE programs, as opposed to the
OPM compatible programs produced by DFTACTGRP(*YES). So we have to specify
DFTACTGRP(*NO)

Note that if you are compiling the program with the CRTBNDRPG command, when you first prompt
the command you will not see the Binding Directory parameter. This will not appear until you have
entered *NO for the DFTACTGRP parameter - not even if you press F10.

IBM supplies a Binding Directory for each compiler. These are used to allow the Binder to locate the
required run-time routines. The compiler automatically passes the name of this Binding Directory to
the Binder, there is no need for the programmer to specify it.

The C compiler operates in a similar fashion. The name of its Binding Directory is (you guessed it!)
QC2LE. By specifying that Directory we enable the Binder to link us to the C run-time library as well
as the RPG one.

If we want to run the program in an Activation group other than QILE we can specify the ACTGRP
parameter on the H spec.

© Copyright Partner400, 2002. Did You Know ...... Page 33-34


Mapping Trigger Buffers
A very practical use of pointers
Use externally described DS(s) to map record images to field names
Use pointers to base the DS and map it to the trigger buffer

Much simpler and safer than laying out the whole buffer
And changes in V5 render many such hard coded solutions obsolete
In fact they may not even work !!

D TrgBufferLen S 10I 0
D ORecord E DS Based(TOldPtr) ExtName(Product)
D Prefix(O_)
D NRecord E DS Based(TNewPtr) ExtName(Product)
C *ENTRY PLIST
C PARM TrgBuffer
C PARM TrgBufferLen
C Eval TOldPtr = %ADDR(TrgBuffer) + TOldOffSet
C Eval TNewPtr = %ADDR(TrgBuffer) + TNewOffSet

We haven't got the time to teach you about Triggers, but those of you familiar with them may find this
example of mapping the Before and After buffers useful.

V5R1 introduced a "small problem" for some users of triggers. Although IBM has always said that
the offsets should be used to locate the record images, many RPGers have continued to hard code
their data structures on the assumption that they would follow the 16 byte reserved area. It seems
that at V5R1 this can no longer be guaranteed. On the following pages we will show you how to do it
the safe way using pointers and the image offsets.

Through the use of pointers and based externally described data structures, as illustrated in this
example, you can make the programming to map the trigger buffer record images to record format
field names a breeze! Note that we have prefixed the field names in the data structure for the old
record image with O_ to distinguish them from the fields in the new record image.

If your records include null capable fields, you can map the null flag array in a similar fashion.

Note that this example uses pointer addition, which was introduced into the compiler at V3R7. You
could still use pointers on systems prior to V3R7, but it would require some additional code to set the
pointer addresses.

Note: Of course, the TrgBufferLen field would need to be defined on a D spec (as a 10 I) somewhere
in the program. It was omitted from this example due to space considerations. Also, note that Fields
is a named constant containing the number of fields in the record format. Unfortunately, the value of
this field must be manually set for each case. If (as in our application's case) there are no null
capable fields in the record format, you could omit all the null map information.

© Copyright Partner400, 2002. Did You Know ...... Page 35-36


Conditional Compilation Directives
Used to condition which source lines are compiled
For example to only include additional debug logic when testing

Conditions can be set on the compile commands


Or in the source itself via the /DEFINE directive
More on this in a moment

/IF DEFINED(TESTING)
* Insert special degugging code here
/ENDIF

/IF DEFINED(CANADA)
* Canadian tax calcs
/ELSEIF DEFINED(MEXICO)
* Mexican tax calcs
/ELSE
* USA tax calcs
/ENDIF

The examples on this chart will rely on the condition (e.g., Testing, Canada, Mexico) being set on
the compile command - either CRTBNDRPG or CRTRPGMOD.

On the next charts, we will have some examples of setting the condition inside the source member
itself.

© Copyright Partner400, 2002. Did You Know ...... Page 37-38


Conditional Compilation Directives
This technique allows multiple source types (e.g. D & C specs) in a
single /COPY member.

/IF DEFINED(DSPEC)
If the member COPYCODE
D MyData ............. contains this:
D MoreData ...........
/ENDIF
/IF DEFINED(CSPEC)
C Eval MyData = MyData + 1
C Eval MoreData = 0
/ENDIF
Then the main program
/DEFINE DSPEC can do this:
/COPY CopyCode
< Only the D spec code will be copied here
/UNDEFINE DSPEC
. . .
/DEFINE CSPEC
/COPY CopyCode
< and only the C spec code here

Because RPG IV does not support the specifcation sorting feature of RPT member types, using
Conditional Compiler Directives like these provide a workaround to meet this need. It is necessary
to put 2 /Copy statements in the program (such as in the bottom example). But it does still allow you
to keep the D specs and the C specs in the same member to be copied in.

© Copyright Partner400, 2002. Did You Know ...... Page 39-40


Sample Source Listing
1 * Main program source
2
3 D MainProgData S 20A
4
5 /DEFINE DSPEC
6 * Copy D specs only
7 /COPY CopyCode
*--------------------------------------------------------------
* RPG member name . . . . . : COPYCODE Identification of Copy
* External name . . . . . . : HSBC/QRPGLESRC(COPYCODE) member is printed as
* Last change . . . . . . . : 00/00/00 00:00:00 normal
*--------------------------------------------------------------
8+
9+ /IF DEFINED(DSPEC)
10+ * Definitions used by Subroutine "Demo"
11+D MyData S 5P 0 Inz
12+D MoreData S 7P 0 Inz
13+ /ENDIF Note that the compiler
14+ /IF DEFINED(CSPEC) directives are printed but
15+ /ENDIF no C-Specs are copied
16+
17
18 D MoreProgData S 20A
19
20 /UNDEFINE DSPEC

Sample Source Listing (Cont.)


21
22 * Regular C-specs
23
24 C Eval MoreProgData = MainProgData
25 C ExSr Demo
26 C Eval *InLR = *On
27
28 /DEFINE CSPEC
29 * Copy C specs only
30 /COPY CopyCode
*--------------------------------------------------------------
* RPG member name . . . . . : COPYCODE
* External name . . . . . . : HSBC/QRPGLESRC(COPYCODE)
* Last change . . . . . . . : 00/00/00 00:00:00
*--------------------------------------------------------------
31+
32+ /IF DEFINED(DSPEC)
33+ /ENDIF Again the compiler directives
are printed but no D-Specs.
34+ /IF DEFINED(CSPEC)
35+C Demo BegSr
36+C Eval MyData = MyData + 1
37+C Eval MoreData = 0
38+C EndSr
39+ /ENDIF
40+

© Copyright Partner400, 2002. Did You Know ...... Page 41-42


Conditional Compilation & Prototypes
Code the Prototype(s) directly in the subprocedure source
Then use conditional compilation directives to "extract" them for use in
programs that will use the procedure(s).

When /Copy'd with the "OnlyProtos" condition set


The H spec is ignored
The Prototype is copied
The compiler skips to end-of-file and continues with the main source

/If Not Defined(OnlyProtos)


H DatFmt(*ISO) NoMain
/EndIf
D EndOfMonth Pr D DatFmt(*ISO)
D WorkDate D DatFmt(*ISO) Value
/If Defined(OnlyProtos)
/EOF
/EndIf

The chart above shows the relevant portion of the subprocedure source file.

When compiling this source we would not set the "OnlyProtos" condition. The compiler proceeds as
follows:

The test IF NOT DEFINED(OnlyProtos) is true (because the condition is not set) and the H-spec is
therefore included in the compile.

The second directive IF DEFINED(OnlyProtos) is not true and so the compiler ignores the lines
enclosed within the IF/ENDIF and therefore will not jump to the end of file but rather will continue
processing the source as normal.

Any program that wishes to use the subprocedures contained in the source member can do so by
including code similar to the following:

/Define OnlyProtos

/Copy sourcefilename

© Copyright Partner400, 2002. Did You Know ...... Page 43-44


Check out the RPG IV Redbook
IBM Redbook SG24-5402
You can read it on-line, download the
PDF file, or order hardcopy
Who Knew You Could Do That
Includes worked examples of with RPG IV?
A Sorcerer's Guide to System Access and
TCP/IP Sockets More

CGI programming
Using the C function library
ILE Error handling
and much more .... SG24-5402
Don't forget to download the source files
for the examples!
Use our link to get easy instructions on
downloading everything from IBM's site: International Technical Support Organization
www.partner400.com/RPGRedbook.htm Rochester, Minnesota

© Copyright Partner400, 2002. Did You Know ...... Page 45-46

Potrebbero piacerti anche