Upload
phungduong
View
217
Download
0
Embed Size (px)
Citation preview
Paul TuohyComCon System i Developer5, Oakton Court, BallybrackCo. DublinIreland
Phone: +353 1 282 6230e-Mail: [email protected]: www.systemideveloper.com www.ComConAdvisor.com
ComConProducers of the RPG & DB2 Summit
RPG Tricks and Techniques
ComConPaul Tuohy
Paul Tuohy has worked in the development of IBM Midrange applications since the �70s. He has been IT manager for Kodak Ireland Ltd. and Technical Director of Precision Software Ltd. and is currently CEO of ComCon, a midrange consultancy company based in Dublin, Ireland. He has been teaching and lecturing since the mid-�80s.
Paul 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 writes regular articles for many publications and is one of the quoted industry experts in the IBM Redbook "Who knew you could do that with RPG IV?".
Paul is one of the co-founders of System i Developer and is also an award winning speaker who speaks regularly at US Common and other conferences throughout the world.
© ComCon and System i Developer, LLC 2006-2011 -1-
ComConDisclaimer
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.
Have you visited SystemiDeveloper.com?
Check it out for the latest and greatest in System i RPG and database education
ComConAgenda
Basic Guidelines
H Spec
Compiler Directives
Managing Prototypes
Integers
Varying Length Fields
Indicators
Sorting Arrays
A couple of BIFs
Keys in Free Form
MOVE/MOVEL/MOVEA in free form
Static
Use offsets when told to
Record Locking
Dynamic memory
Constraints
© ComCon and System i Developer, LLC 2006-2011 -2-
ComCon
CL0N01Factor1+++++++Opcode(E)+Factor2+++++++Result++++++++Len++D+..
C HRS IFLE 40
C HRS MULT(H) RATE PAY 7 2
C ELSE
C RATE MULT 40 REGPAY 7 2
C HRS SUB 40 HRSOT 3 0
C RATE MULT 1.5 OTRATE 3 2
C HRSOT MULT(H) OTRATE OTPAY 7 2
C REGPAY ADD OTPAY PAY 7 2
C ENDIF
CL0N01Factor1+++++++Opcode(E)+Factor2+++++++Result++++++++Len++D+..
C HRS IFLE 40
C HRS MULT(H) RATE PAY 7 2
C ELSE
C RATE MULT 40 REGPAY 7 2
C HRS SUB 40 HRSOT 3 0
C RATE MULT 1.5 OTRATE 3 2
C HRSOT MULT(H) OTRATE OTPAY 7 2
C REGPAY ADD OTPAY PAY 7 2
C ENDIF
CL0N01Factor1++++++Opcode(E)Extended-factor2+++++++++++++++++++++
C if (hours <= 40)
C eval(H) weeklyPay = rate * hours
C else
C eval(H) weeklyPay = ((hours-40) * (rate*1.5))
C + (rate * 40)
C endIf
CL0N01Factor1++++++Opcode(E)Extended-factor2+++++++++++++++++++++
C if (hours <= 40)
C eval(H) weeklyPay = rate * hours
C else
C eval(H) weeklyPay = ((hours-40) * (rate*1.5))
C + (rate * 40)
C endIf
if (hours <= 40);
eval(H) weeklyPay = rate * hours;
else;
eval(H) weeklyPay = ((hours - 40) * (rate * 1.5)) + (rate * 40);
endIf;
if (hours <= 40);
eval(H) weeklyPay = rate * hours;
else;
eval(H) weeklyPay = ((hours - 40) * (rate * 1.5)) + (rate * 40);
endIf;
The Multiple Faces of RPG
ComConBasic Guidelines
Be Free
Code EVERYTHING in free form- Even if you are adding two lines of code to an old RPG II style program
Mind your language
Use proper names- customerName not custNam not cusNo not wkCsNo
- messageType not msgType
Speak the same language as the rest of the world- Table instead of Physical File- Index instead of Logical File
Learn and adopt from other languages- /INCLUDE instead of /COPY
- Constants in capitals
Use the modern tool set (WDSC/RDi)
The tools will help with everything new
Embrace what is new
Learn new habits
© ComCon and System i Developer, LLC 2006-2011 -3-
ComConH Spec
The H Spec has a new lease of life in RPG IV
Defaults for program
Debugging
Compiler options
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 member for any standard H-specs, not a Data Area
© ComCon and System i Developer, LLC 2006-2011 -4-
ComCon
H option(*srcStmt :*noDebugIO)
H datFmt(*ISO) timFmt(*ISO) datEdit(*YMD-)
H decPrec(63)
H copyRight('This is mine, all mine!')
H option(*srcStmt :*noDebugIO)
H datFmt(*ISO) timFmt(*ISO) datEdit(*YMD-)
H decPrec(63)
H copyRight('This is mine, all mine!')
Compiler Options on H-spec
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.
Unsupported keywords:
DBGVIEW, OUTPUT, REPLACE, DEFINE, PGM, SRCFILE, SRCMBR, TGTRLS
Use Option(*SrcStmt:*NoDebugIO)
Match program statement numbers with source line numbers !!
Easier for debugging and end user support
Skip the individual field steps for Input and Output specs
Faster step function during debugging
On the H spec, separate multiple options with a colon (:) � e.g 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).
© ComCon and System i Developer, LLC 2006-2011 -5-
ComConCompiler Directives
New compiler directives
Control which statements in your source are to be used
Set (/DEFINE) and clear (/UNDEFINE) a condition-name
Condition names may also be set on the CRTBNDRPG and CRTRPGMOD commands
Include source based on the status of a condition-name
/IF {NOT} DEFINED(condition-name)
/ELSEIF {NOT} DEFINED(condition-name)
/ENDIF
Skip to the end of the current source
/EOF
Many uses
including maintaining a common source for different versions of an application
Copy Members may be nested
ComConConditional Compilation (1 of 2)
A technique to allow multiple spec types in a single copy member
Uses nested copies- SPSSRON contains free form calculations- FORMATS and PROTOTYPES contain D specs
- STDHSPEC contains H specs
/if defined(DOCSPEC)
/include QCPYSRC,SPSSRON
/elseIf defined(DODSPEC)
/include QCPYSRC,FORMATS
/include QCPYSRC,PROTOTYPES
/if defined(CGIPGM)
/include QCPYSRC,PROTOTYPEB
/include QCPYSRC,USEC
/endIf
/unDefine DODSPEC
/define DOCSPEC
/else
/include STDHSPEC
/define DODSPEC
/endIf
/if defined(DOCSPEC)
/include QCPYSRC,SPSSRON
/elseIf defined(DODSPEC)
/include QCPYSRC,FORMATS
/include QCPYSRC,PROTOTYPES
/if defined(CGIPGM)
/include QCPYSRC,PROTOTYPEB
/include QCPYSRC,USEC
/endIf
/unDefine DODSPEC
/define DOCSPEC
/else
/include STDHSPEC
/define DODSPEC
/endIf
If the member STANDARDcontains this
If the member STANDARDcontains this
© ComCon and System i Developer, LLC 2006-2011 -6-
ComConConditional Compilation (2 of 2)
The main program includes multiple copy directives for the same copy member
STANDARD � on the previous slide
/include QCPYSRC,STANDARD
< Only the H spec code will be copied here >
F
F
/include QCPYSRC,STANDARD
< Only the D spec code will be copied here >
D
D
/free
(Lots of most excellent Code)
/include QCPYSRC,STANDARD
< and only the C spec code will be copied here >
/include QCPYSRC,STANDARD
< Only the H spec code will be copied here >
F
F
/include QCPYSRC,STANDARD
< Only the D spec code will be copied here >
D
D
/free
(Lots of most excellent Code)
/include QCPYSRC,STANDARD
< and only the C spec code will be copied here >
Then the copy member can be used like this
Then the copy member can be used like this
© ComCon and System i Developer, LLC 2006-2011 -7-
ComConPredefined Definition Names
Compiler has a few predefined definition names
*CRTBNDRPG � if compiling using the CRTBNDRPG command
*CRTRPGMOD � if compiling using the CRTRPGMOD command
*ILERPG � if compiling using the ILE RPG IV compiler- as opposed to the Visualage for RPG compiler
*VxRxMx � the target release version (or later) of OS/400- Starting with a target of V4R4M0
/if defined(*CRTBNDRPG)
H dftActGrp(*NO) actGrp('MYSYS') /endIf
H bndDir('MYBNDDIR') H option(*srcStmt :*noDebugIO) H datFmt(*ISO) timFmt(*ISO) datEdit(*YMD-)
H decPrec(63)
H copyRight('This is mine, all mine!')
/if defined(*CRTBNDRPG)
H dftActGrp(*NO) actGrp('MYSYS') /endIf
H bndDir('MYBNDDIR') H option(*srcStmt :*noDebugIO) H datFmt(*ISO) timFmt(*ISO) datEdit(*YMD-)
H decPrec(63)
H copyRight('This is mine, all mine!')
When you use the Binding Directory support, the details of your directory are included in the module in the same way as the system run-time binding directories that the compiler supplies. You can see the binding directories that will be used by displaying the module (DSPMOD) on the page with Reference System Objects detail.
© ComCon and System i Developer, LLC 2006-2011 -8-
ComConManaging Prototypes
SRC-PF FILEPROCSSRC-PF FILEPROCS
D getProductData PR
D setProductData PR
// etc.
D getProductData PR
D setProductData PR
// etc.
D getSalesData PR
D setSalesData PR
// etc.
D getSalesData PR
D setSalesData PR
// etc.
/include FILEPROCS,PFILE01A/include FILEPROCS,PFILE02A/include FILEPROCS,PFILE03A
/include FILEPROCS,PFILE01A/include FILEPROCS,PFILE02A/include FILEPROCS,PFILE03A
PFILE01A
PFILE02A
PFILEPROCS
SRC-PF UTILITYSRC-PF UTILITY
D getMessage PR
D sendMessage PR
// etc.
D getMessage PR
D sendMessage PR
// etc.
/include UTILITY,PUTIL01A/include UTILITY,PUTIL02A
/include UTILITY,PUTIL01A/include UTILITY,PUTIL02A
PUTIL01A
PUTILITY
SRC-PF QPROTOSRCSRC-PF QPROTOSRC
D myProgram001A PRD myProgram001A PR
/include QPROTOSRC,PGM001A/include QPROTOSRC,PGM002A/include QPROTOSRC,PGM003A
/include QPROTOSRC,PGM001A/include QPROTOSRC,PGM002A/include QPROTOSRC,PGM003A
PGM001A
PPGMPROTO
D myProgram002A PRD myProgram002A PR
PGM002A
D myProgram003A PRD myProgram003A PR
PGM003A
SRC-PF QINCLUDESSRC-PF QINCLUDES
/include FILEPROCS,PFILEPROCS/include UTILITY,PUTILITY/include QPROTOSRC,PPGMPROTO
/include FILEPROCS,PFILEPROCS/include UTILITY,PUTILITY/include QPROTOSRC,PPGMPROTO
BASEINFO
Bearing in mind that an application may end up having thousands of prototypes, just one source member containing all prototypes for the application will very quickly become unmanageable. The important point is that all programs should only have one copy directive to include all prototypes. The ability to have nested copy directives makes the maintenance of prototypes very straight forward.
That copy member included in every source contains multiple copy directives to include other members containing prototype definitions. These other members would be:-
� A copy member containing prototypes for all dynamic calls� A copy member per service program containing prototypes for all procedures
exported from the service program
� Or, for very large service programs, have a copy member per module in the service program. Then have a copy member for the service program that contains copy directives for all the module prototype members.
You want to achieve the best of both worlds in that only one copy directive is required to have all prototypes included but the actual prototypes themselves are stored in multiple easy to manage members.
But why stop with just prototypes? Why not include copy members containing standard definitions of data structures and named constants? All of these can be included with just one copy directive.
© ComCon and System i Developer, LLC 2006-2011 -9-
ComConUse Integers
Integers have a data type of I or U
I = Signed
U = Unsigned
Replaces the old B (Binary) data type
Has a broader range then binary (for the same storage)
No conversion to/from packed (as with binary)
Use for compatibility with other languages
C, Java and MI (and CL in V5R3)
Used extensively with C functions and APIs
Better performance for math operations
Faster then packed (or zoned or binary)- But would you notice the difference?
Use 10I 0 as your default work field size
If you do not need decimals/fractions
For numeric parameters
ComConInteger Details
RPG Bytes Range API C and Java
3 i 0 1 -128 to 127 BIN1 char
5 i 0 2 -32,768 to 32,767 BIN2 short
10 i 0 4 -2,147,483,648 to 2,147,483,647 BIN4 long or int
20 i 0 8 -9,223,372,036,854,775,808 to9,223,372,036,854,775,807
BIN8 long
3 u 0 1 0 to 255 UBIN1 Unsigned char
5 u 0 2 0 to 65535 UBIN2 short
10 u 0 4 0 to 4,294,967,295 UBIN4 long
20 u 0 8 0 to 18,446,744,073,709,551,615 UBIN8 long long
Integers are identified in different ways in different places
RPG
API documentation
C and Java- The same
© ComCon and System i Developer, LLC 2006-2011 -10-
ComConVarying Length Fields
D firstName s 10a inz('Paul')
D lastName s 10a inz('Tuohy')
D v_firstName s 10a varying inz('Paul')
D v_lastName s 10a varying inz('Tuohy')
D fullName s 21a varying
fullName = %trimR(firstName) + ' ' + %trimR(lastName);
fullName = v_firstName + ' ' + v_lastName;
fullName = '';
%len(fullName) = 0;
fullName = *blanks; // fullName now has 21 spaces
D firstName s 10a inz('Paul')
D lastName s 10a inz('Tuohy')
D v_firstName s 10a varying inz('Paul')
D v_lastName s 10a varying inz('Tuohy')
D fullName s 21a varying
fullName = %trimR(firstName) + ' ' + %trimR(lastName);
fullName = v_firstName + ' ' + v_lastName;
fullName = '';
%len(fullName) = 0;
fullName = *blanks; // fullName now has 21 spaces
Excellent for string handling
Reduces requirement for %TRIMx functions
The length is set when data is moved into it
The %LEN function can also be used to reset the length
Be careful using *blanks
ComConCAT Functionality with Varying Fields
The fixed format CAT operation allowed you to specify trailing blanks
F1 field is right trimmed and trailing blanks added
Trailing blanks specified after F2- As a literal or constant
- As a numeric field
Define varying field for padding
Length should be maximum length required
Initialize to all blanks
Set length of padding field to required trailing blanks and concatenate
D firstName s 10a inz('Paul')
D lastName s 10a inz('Tuohy')
D padding s 500a varying inz(*blanks)
%len(padding) = 10;
fullName = %trimR(firstName) + padding + %trimR(lastName);
D firstName s 10a inz('Paul')
D lastName s 10a inz('Tuohy')
D padding s 500a varying inz(*blanks)
%len(padding) = 10;
fullName = %trimR(firstName) + padding + %trimR(lastName);
D firstName s 10a inz('Paul')
D lastName s 10a inz('Tuohy')
C firstName Cat lastName:10 fullName
D firstName s 10a inz('Paul')
D lastName s 10a inz('Tuohy')
C firstName Cat lastName:10 fullName
© ComCon and System i Developer, LLC 2006-2011 -11-
ComConA quick word about Varying Length fields
Varying length fields have two components
The current length of the field - a two byte binary (5i 0) or a four byte binary (10i 0)
� Depending on the defined length of the field
The actual data
A varying length field requires two (or four) more bytes of storage then the defined length
Be careful when using varying length fields in data structures
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
00 16 T H I S T H E D A T A
D DS
D webOut 2048a varying
D webDataLen 5i 0 overlay(webOut)
D webData 2048a overlay(webOut:*next)
webOut = 'THIS IS THE DATA';
D DS
D webOut 2048a varying
D webDataLen 5i 0 overlay(webOut)
D webData 2048a overlay(webOut:*next)
webOut = 'THIS IS THE DATA';
© ComCon and System i Developer, LLC 2006-2011 -12-
ComConUse Named Indicators
Use real names for your display and print file indicators !
Fdisplay CF E workstn indDS(dsp)
D dsp DS qualified
D F3_Exit n overLay(dsp:3)
D F12_Cancel n overLay(dsp:12)
D resetErrorIndicators...
D 3a overLay(dsp:31)
D error n overLay(dsp:31)
D startDateError...
D n overLay(dsp:32)
D endDateError...
D n overLay(dsp:33)
dsp.resetErrorIndicators = *zeros;
if (startDate < today);
dsp.startDateError = *on ;
endIf;
dsp.endDateError = (endDate < startDate);
dsp.error = dsp.startDateError or dsp.endDateError;
exFmt testRec;
if (dsp.F3Exit or dsp.F12_Cancel);
Fdisplay CF E workstn indDS(dsp)
D dsp DS qualified
D F3_Exit n overLay(dsp:3)
D F12_Cancel n overLay(dsp:12)
D resetErrorIndicators...
D 3a overLay(dsp:31)
D error n overLay(dsp:31)
D startDateError...
D n overLay(dsp:32)
D endDateError...
D n overLay(dsp:33)
dsp.resetErrorIndicators = *zeros;
if (startDate < today);
dsp.startDateError = *on ;
endIf;
dsp.endDateError = (endDate < startDate);
dsp.error = dsp.startDateError or dsp.endDateError;
exFmt testRec;
if (dsp.F3Exit or dsp.F12_Cancel);
The display/printer file MUST specify the INDARA keyword
The display/printer file MUST specify the INDARA keyword
Logical Expressions!Logical Expressions!
This example illustrates the use of the INDDS (indicator Data Structure).
Note the definition of the DSPIND data structure. When this is specified on the F spec, the programmer MUST use the indicators in the data structure and NOT the numbered indicators to control this file. For example, in this program, if the programmer turned on indicator *IN31 directly in the program logic, it would have zero impact on the display file. The program logic must refer to the indicator as "Error" to turn it on or off in order for it to have an impact on the display file.
The keyword INDARA must be specified for the display/printer file. This does not normally have any impact on the RPG program. It specifies that indicators should "travel" in their own area, separate from the normal data stream. As a result any CLEAR or RESET operations applied to a record format will not affect indicators in an INDARA file, but would have done so without this keyword. If you use CLEAR and RESET you may need to add aditional code to handel the indicators.
This is the DDS for the display file used in the RPG example:
INDARA
R TESTREC CF03(03) CF12(12)
5 11'Start Date: . .'
STARTDATE L 5 27DATFMT(*USA)
32 DSPATR(RI PC)
32 5 39'Date cannot be earlier than today'
7 11'End Date: . . .'
ENDDATE L 7 27DATFMT(*USA)
33 DSPATR(RI PC)
33 7 39'Date must be later than Start Date'
31 10 21'Please correct above error'
© ComCon and System i Developer, LLC 2006-2011 -13-
ComConAn Alternative to Named Indicators
Not in a position to switch your old programs to use INDDS ?
Try using this technique to make your code more obvious
Since indicators can be referenced through the *IN(nn) array- Why not use named constants to identify their purpose
- Like so:
// Indicator Constants
D F3_EXIT C 3
D F12_CANCEL C 12
D ERROR C 31
D START_DATE_ERROR...
D C 32
D END_DATE_ERROR...
D C 33
if (startDate < today);
*In(START_DATE_ERROR) = *On;
endIf;
*in(END_DATE_ERROR) = (endDate < startDate);
*in(ERROR) = *in(START_DATE_ERROR) or *in(END_DATE_ERROR);
// Indicator Constants
D F3_EXIT C 3
D F12_CANCEL C 12
D ERROR C 31
D START_DATE_ERROR...
D C 32
D END_DATE_ERROR...
D C 33
if (startDate < today);
*In(START_DATE_ERROR) = *On;
endIf;
*in(END_DATE_ERROR) = (endDate < startDate);
*in(ERROR) = *in(START_DATE_ERROR) or *in(END_DATE_ERROR);
If you cannot make use of the INDDS, I strongly urge you to try this technique instead. Since all constant references are resolved at compile time, the resulting code is every bit as fast as the old-fashioned *IN04 type references. The nice thing is you can gradually introduce it to existing programs as you are working on them and since the resulting executable code is identical, there is no additional test overhead.
You will be amazed at how much more readable your programs become. And more readable = more maintainable = more productive = more time to do interesting stuff !!
Isn't the intent of the code above a lot more obvious than if we had coded:
C If StartDate < Today
C Eval *In32 = *On
C EndIf
C Eval *In33 = EndDate < StartDate
C Eval *In31 = *In32 or *In33
© ComCon and System i Developer, LLC 2006-2011 -14-
ComConBased Data Structures (to V5R4)
A Based DS can be used to define �standard� data
A based DS does not take up memory
Great for API formats
Great for prototypes
Leads to the use of qualified data structures
D baseAddress DS based(baseptr) qualified
D street1 30
D street2 30
D city 20 varying
D state 2
D zip 5
D zipPlus 4
D invoiceInfo DS qualified
D mailAddr likeDS(baseAddress)
D shipAddr likeDS(baseAddress)
D baseAddress DS based(baseptr) qualified
D street1 30
D street2 30
D city 20 varying
D state 2
D zip 5
D zipPlus 4
D invoiceInfo DS qualified
D mailAddr likeDS(baseAddress)
D shipAddr likeDS(baseAddress)
ComConBased Data Structures � Better in V6R1
A Based DS can be defined using TEMPLATE keyword
TEMPLATE allows subfields to be initialized
Cannont �accidently� reference subfields in template
D baseAddress DS template qualified
D street1 30
D street2 30
D city 20 varying
D state 2 inz('TX')
D zip 5
D zipPlus 4
D invoiceInfo DS qualified
D mailAddr likeDS(BaseAddress) inz(*likeDS)
D shipAddr likeDS(BaseAddress) inz(*likeDS)
D baseAddress DS template qualified
D street1 30
D street2 30
D city 20 varying
D state 2 inz('TX')
D zip 5
D zipPlus 4
D invoiceInfo DS qualified
D mailAddr likeDS(BaseAddress) inz(*likeDS)
D shipAddr likeDS(BaseAddress) inz(*likeDS)
© ComCon and System i Developer, LLC 2006-2011 -15-
ComConReferencing Based Structures
Where to store based structured
In same member as prototypes- If structure relates to a program or procedure
In �standard� copy members
Should be included in ALL programs/procedures
Same as prototypes
/include QINCLUDES,BASEINFO
D invoiceInfo DS qualified
D mailAddr likeDS(baseAddress) inz(*likeDS)
D shipAddr likeDS(baseAddress) inz(*likeDS)
/include QINCLUDES,BASEINFO
D invoiceInfo DS qualified
D mailAddr likeDS(baseAddress) inz(*likeDS)
D shipAddr likeDS(baseAddress) inz(*likeDS)
ComConOverlaying a DS & Unnamed Fields
Originally, the Overlay keyword only applied to subfields
Now, you can overlay the DS itself
Makes a great alternative to using compile-time data
Initialize the data near the array definition itself
No need to chase to the end of the source member
Note that the DS subfields do not need to be named
But can still have INZ values!
D compileData DS
D 27a inz('January February March ')
D 27a inz('April May June ')
D 27a inz('July August September')
D 27a inz('October November December ')
D monthNames 9a overlay(compileData) dim(12)
D compileData DS
D 27a inz('January February March ')
D 27a inz('April May June ')
D 27a inz('July August September')
D 27a inz('October November December ')
D monthNames 9a overlay(compileData) dim(12)
© ComCon and System i Developer, LLC 2006-2011 -16-
ComConUsing SORTA with Group Fields
Want to sort an array with different keys?
Group fields can provide an answer
Be sure to specify the DIM at the group field level
Then the array can be sorted on any of the subfields
D addressInfo DS
// Note that Dim is specified at the group field level
D addressData 52a dim(1000)
D streetA 30a overlay(addressData)
D cityA 20a overlay(addressData: *next)
D stateA 2a overlay(addressData: *next)
/free
sortA %subArr(cityA:1:noLoaded); // Sort into City sequence
sortA %subArr(stateA:1:noLoaded); // Sort in State sequence
D addressInfo DS
// Note that Dim is specified at the group field level
D addressData 52a dim(1000)
D streetA 30a overlay(addressData)
D cityA 20a overlay(addressData: *next)
D stateA 2a overlay(addressData: *next)
/free
sortA %subArr(cityA:1:noLoaded); // Sort into City sequence
sortA %subArr(stateA:1:noLoaded); // Sort in State sequence
Note that when using this technique all of the other fields in the array (i.e. those that are part of the group) will be "pulled along" with their associated values.
ASCEND or DESCEND can be specified as normal along with the DIM keyword. So, while you can sort on any of the fields in the group, you can only sort ascending OR descending sequence on any given array.
The %SUBARR BIF was introduced in V5R3. In this example NoLoaded contains the number of elements loaded to the array.
© ComCon and System i Developer, LLC 2006-2011 -17-
ComConWhy Use Large Arrays to Store Data?
Load Once � Reference Often
Subfiles
Load the data to an array
Load the subfile from the array
Users can select �sort by� columns
An example in a moment
Tables
Instead of constantly chaining to tables
Load the table to an array
Use %Lookup instead of CHAIN
Not a good option for �volatile� data
© ComCon and System i Developer, LLC 2006-2011 -18-
ComConUse an Array to Store Subfile Data
Subfiles
Load the data (usually from databases) to an array- Each element is a subfile record and each subfield is an �array mapping�
Load the subfile from the array
Users can select �sort by� columns- Program uses SORTA and reloads the subfile.
d subRecData E Ds extName(SUBSORTD:SUBREC)
d Ds
d allSubRec like(subRecData)
d dim(9999) ascend
d arrOption like(option) overLay(allSubRec)
d arrCode like(code) overLay(allSubRec:*next)
d arrName like(name) overLay(allSubRec:*next)
d subRecData E Ds extName(SUBSORTD:SUBREC)
d Ds
d allSubRec like(subRecData)
d dim(9999) ascend
d arrOption like(option) overLay(allSubRec)
d arrCode like(code) overLay(allSubRec:*next)
d arrName like(name) overLay(allSubRec:*next)
A REF(*LIBL/CUST)
A R SUBREC SFL
A OPTION 1A B 7 3
A 31 DSPATR(RI PC)
A CODE R O 7 11
A NAME R O 7 22
A REF(*LIBL/CUST)
A R SUBREC SFL
A OPTION 1A B 7 3
A 31 DSPATR(RI PC)
A CODE R O 7 11
A NAME R O 7 22
This is the relevant DDS for the display file:-
A R SUBREC SFL
A 54 SFLNXTCHG
A OPTION 1A B 7 3
A 31 DSPATR(RI PC)
A CODE R O 7 11
A NAME R O 7 22
A R SUBCTL SFLCTL(SUBREC)
A OVERLAY
A 51 SFLDSP
A 52 SFLDSPCTL
A 53 SFLCLR
A 51 SFLEND(*MORE)
A SFLSIZ(0050)
A SFLPAG(0014)
A RTNCSRLOC(*RECNAME &CURSORREC &CURSORFLD)
A SFLCSRRRN(&ATRECORD)
A POSITIONAT 4S 0H SFLRCDNBR
A RRN 4S 0H
A ATRECORD 5S 0H
A CSRREC 10 H
A CSRFLD 10 H
© ComCon and System i Developer, LLC 2006-2011 -19-
ComConSorting a Subfile Array
d subRecData E Ds extName(SUBSORTD:SUBREC)
d Ds
d allSubRec like(subRecData)
d dim(9999) ascend
d arrOption like(option) overLay(allSubRec)
d arrCode like(code) overLay(allSubRec:*next)
d arrName like(name) overLay(allSubRec:*next)
d subRecData E Ds extName(SUBSORTD:SUBREC)
d Ds
d allSubRec like(subRecData)
d dim(9999) ascend
d arrOption like(option) overLay(allSubRec)
d arrCode like(code) overLay(allSubRec:*next)
d arrName like(name) overLay(allSubRec:*next)
select;
when cursorFld = 'CCODE';
sortA %subArr(arrCode:1:recordsInSubfile);
when cursorFld = 'ANAME';
sortA %subArr(arrName:1:recordsInSubfile);
endSl;
select;
when cursorFld = 'CCODE';
sortA %subArr(arrCode:1:recordsInSubfile);
when cursorFld = 'ANAME';
sortA %subArr(arrName:1:recordsInSubfile);
endSl;
Sort relevant array based on RTNCSRLOC field
Sort relevant array based on RTNCSRLOC field
numRows = 0;
read dataBase;
dow not %EOF(dataBase);
numRows += 1;
allSubRec(numRows) = subRecData;
read dataBase;
endDo;
numRows = 0;
read dataBase;
dow not %EOF(dataBase);
numRows += 1;
allSubRec(numRows) = subRecData;
read dataBase;
endDo;
for RRN = 1 to numRows;
subRecData = allSubRec(RRN);
write subRec;
endFor;
for RRN = 1 to numRows;
subRecData = allSubRec(RRN);
write subRec;
endFor;
Load data to arrayLoad subfile from array
Load data to arrayLoad subfile from array
ComConCode the norm...
Program the exception
monitor;
value = value/rate;
on-error;
value = 0;
endMon;
monitor;
value = value/rate;
on-error;
value = 0;
endMon;
if (rate <> 0);
value = value/rate;
else;
value = 0;
endIf;
if (rate <> 0);
value = value/rate;
else;
value = 0;
endIf;
Old RPG habits die hardOld RPG habits die hard
© ComCon and System i Developer, LLC 2006-2011 -20-
ComConFOR Operation
Particularly useful with arrays
Make use of that %ELEM BIF
A FOR Loop can
Increment
Decrement
Have whatever incremental step you want
D price S 3p 2 dim(5)
D qty S 3p 0 dim(5)
D total S 11p 0
for i = 1 to %elem(price);
total = total + ( price(i) * qty(i) );
endFor;
D price S 3p 2 dim(5)
D qty S 3p 0 dim(5)
D total S 11p 0
for i = 1 to %elem(price);
total = total + ( price(i) * qty(i) );
endFor;
FOR <index-name> { = <starting-value> }
{ BY <increment-value> }
{ TO | DOWNTO <limit-value> }
{ loop body }
ENDFOR | END
FOR <index-name> { = <starting-value> }
{ BY <increment-value> }
{ TO | DOWNTO <limit-value> }
{ loop body }
ENDFOR | END
The FOR operation begins a group of operations and indicates the number of times the group will be processed. To indicate the number of times the group of operations is to be processed, specify an index name, a starting value, an increment value, and a limit value. The optional starting, increment, and limit values can be a free-form expressions. An associated END or ENDFOR statement marks the end of the group.
The index name must be the name of a scalar, numeric variable with zero decimal positions. It cannot be an indexed array. The starting-value, increment-value, and limit-value can be numeric values or expressions with zero decimal positions. The increment value, if specified, cannot be zero.
The BY and TO (or DOWNTO) clauses can be specified in either order. Both "BY 2 TO 10" and "TO 10 BY 2" are allowed.
Remember the following when specifying the FOR operation:
The index name cannot be declared on the FOR operation. Use the D spec to define it. � An indexed array element is not allowed as the index field in a FOR operation. � If no starting value is specified, the index name retains the same value it had before
the start of the loop. � If no limit value is specified, the loop repeats indefinitely until it encounters a
statement that exits the loop (such as a LEAVE or GOTO) or that ends the program or procedure (such as a RETURN).
� The LEAVE Operation works in FOR loops just as it does in DO groups.
© ComCon and System i Developer, LLC 2006-2011 -21-
ComConEditing BIFs
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.
%EDITC and %EDITW are invaluable with data for the web
D text S 50a
D balance S 9p 2
text = 'Your balance of ' + %trim(%editC(balance:'A':*curSym))
+ ' is now seriously overdue';
D text S 50a
D balance S 9p 2
text = 'Your balance of ' + %trim(%editC(balance:'A':*curSym))
+ ' is now seriously overdue';
ComCon"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
Although it is one of the �trickier� BIFs, %REPLACE is well worth the effort to master
D message S 100a varying
D salutation S 50a varying
D baseText C 'Dear &S, thank you for your +
D recent order'
read customer;
if not %EOF(customer);
Salutation = %trimR(title) + ' ' + %trimR(firstName);
message = %replace( %trimR(salutation): baseText
: %scan('&S': baseText): 2 );
// Other process
read customer;
endIf;
D message S 100a varying
D salutation S 50a varying
D baseText C 'Dear &S, thank you for your +
D recent order'
read customer;
if not %EOF(customer);
Salutation = %trimR(title) + ' ' + %trimR(firstName);
message = %replace( %trimR(salutation): baseText
: %scan('&S': baseText): 2 );
// Other process
read customer;
endIf;
© ComCon and System i Developer, LLC 2006-2011 -22-
ComConKeys in Free Form
Keys are easier to use in Free Form
FSalesHist IF E K Disk
D salesData Ds likeRec(salesHistR)
chain (custCode: prodCode: 'AB' + Input) salesHist salesData;
FSalesHist IF E K Disk
D salesData Ds likeRec(salesHistR)
chain (custCode: prodCode: 'AB' + Input) salesHist salesData;
FSalesHist IF E K Disk
D salesKey E Ds extname(salesHist:*Key)
D qualified
D salesData Ds likeRec(salesHistR)
chain %KDS(salesKey) salesHist salesData;
FSalesHist IF E K Disk
D salesKey E Ds extname(salesHist:*Key)
D qualified
D salesData Ds likeRec(salesHistR)
chain %KDS(salesKey) salesHist salesData;
FSalesHist IF E K Disk
D salesData Ds likeRec(salesHistR)
C keyList KList
C KFld custCode
C KFld prodCode
C KFld invoice
C keyList chain salesHist salesData
FSalesHist IF E K Disk
D salesData Ds likeRec(salesHistR)
C keyList KList
C KFld custCode
C KFld prodCode
C KFld invoice
C keyList chain salesHist salesData
Regardless of whether you use fixed form or free form, file operation codes may now specify a data structure to be used with the operation. In these examples the record is placed in the SalesData data structure: the data structure must be defined using the LIKEREC keyword and it is implicitly qualified.
The first example shows the traditional fixed form method of defining a key list to be used with a file where the key consists of a number of fields.
The second example shows how a key list may be emulated in the D specs. An externally defined data structure is defined using the optional parameter of *KEY on the EXTNAME key word to indicate that only key fields from the external file are to be included in the data structure. This data structure is then used as an argument for the %KDS BIF on the CHAIN operation. This is slightly better than a key list in that the %KDS keyword makes it clear that a data structure is being used to define the key.
But the third example is the preferred way to do it. Simply provide the list of key fields to be used. You can even use a literal and/or an expression as one of the key fields!
© ComCon and System i Developer, LLC 2006-2011 -23-
ComConThe �problem� with MOVE/MOVEL
There is a problem with MOVE/MOVEL?
You must know what the fields are
You must know the definition of the fields
(thanks to Chris Bucko for asking me to cover this)
C MOVE '3' BARTYP 3
C MOVEL BARTYP LASTYP 3 0
C MOVE 1 LASTYP
C MOVE '1' *INLR
C MOVE '3' BARTYP 3
C MOVEL BARTYP LASTYP 3 0
C MOVE 1 LASTYP
C MOVE '1' *INLR
C MOVE '3' BARTYP
C MOVEL BARTYP LASTYP
C MOVE 1 LASTYP
C MOVE '1' *INLR
C MOVE '3' BARTYP
C MOVEL BARTYP LASTYP
C MOVE 1 LASTYP
C MOVE '1' *INLR
ComConMove in Free Form Guidelines
Character to character:-
EVAL is the same as MOVEL with padding
EVALR is the same as MOVE with padding
Use %SUBSTR if different lengths and no padding- %substr(char10: 1: 6) = char6; // MOVEL
- %substr(char10: 5: 6) = char6; // MOVE
Number to number
Use EVAL
Watch out for numeric overflow- Use MONITOR if in doubt
Number to character
Use %CHAR, %EDITC, %EDITW and, if necessary, %SUBSTR- char10 = %char(number);
- %substr(char10: 1: 6) = %char(number)
Character to number
Use %INT, %DEC, %DECH
© ComCon and System i Developer, LLC 2006-2011 -24-
ComConMOVE in Free Form
Self Explanatory
Don�t need to know field definitions
D charField 3
D numberField 3 0 inz(5)
D integerField 5i 0
D dateField d
D charDate 6 inz('021408')
D numberDate 6 0
charField = %char(numberField); // charField = '5 '
charField = %editc(numberField:'3'); // charField = ' 5'
charField = %editc(numberField:'X'); // charField = '005'
integerField = %int(charField); // integerField = 5
numberField = %dec(charField: 3: 0); // numberField = 5
dateField = %date(charDate:*MDY0)
charDate = %char(%date():*ISO0);
numberDate = %dec(%date():*ISO);
D charField 3
D numberField 3 0 inz(5)
D integerField 5i 0
D dateField d
D charDate 6 inz('021408')
D numberDate 6 0
charField = %char(numberField); // charField = '5 '
charField = %editc(numberField:'3'); // charField = ' 5'
charField = %editc(numberField:'X'); // charField = '005'
integerField = %int(charField); // integerField = 5
numberField = %dec(charField: 3: 0); // numberField = 5
dateField = %date(charDate:*MDY0)
charDate = %char(%date():*ISO0);
numberDate = %dec(%date():*ISO);
ComConWhat About MOVEA?
Why was MOVEA array being used?
To emulate string handling- Use string BIFs instead
Multi dimensional arrays- Use data structure arrays
Storing copies of data- Use %subArr() BIF
But some MOVEA operations are more complex
Make use of data structures- Overlay arrays onto character fields
Write a subprocedure- Which would also make it easier to understand
© ComCon and System i Developer, LLC 2006-2011 -25-
ComConStatic
The oft ignored STATIC keyword
Used in subprocedure D specs
A subprocedures memory is reallocated on every call
Except for data defined with the STATIC keyword
STATIC mean that fields...
That are specific to the subprocedure
And whose state needs to be maintained between calls
Do NOT have to be defined globally
P putSource2...
P B
d PI
d nextSeq s 10i 0 static
/free
nextSeq += 1;
s2.srcSeq = nextSeq;
write source2 s2;
return;
/end-Free
P E
P putSource2...
P B
d PI
d nextSeq s 10i 0 static
/free
nextSeq += 1;
s2.srcSeq = nextSeq;
write source2 s2;
return;
/end-Free
P E
© ComCon and System i Developer, LLC 2006-2011 -26-
ComConAlways Use Offsets!
Many IBM supplied programs have special �buffers�
APIs
Triggers
Usually use Data Structures in RPG
If documentation mentions an offset � USE IT!
Do NOT hard code positions
Bad things can happen
An example
Prior to V5R1 many programmers hard coded from and to positions for before/after image fields in the trigger buffer
In V5R1, IBM changed the rules for the buffer- Each image now starts on a 16 byte boundary
The �lucky� ones got a decimal data error
Although IBM have been warning programmers for years to use the offset values to map trigger buffers, this was not the easiest thing to do in RPG - particularly in RPG/400.
In V5R1, and later releases, failure to heed this warning can burn you badly as the before and after image buffers are not necessarily contiguous. There may be up to 15 bytes of "padding" between the two in order to ensure that the beginning of the buffer starts on a 16 byte boundary.
© ComCon and System i Developer, LLC 2006-2011 -27-
ComConMapping the Before and After Images
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 only stand a 1 in 16 chance of working !!
D oldFile E DS extName(fileName) qualified
D based(oldPtr)
D newFile E DS extName(fileName) qualified
D based(newPtr)
D oldPtr S *
D newPtr S *
oldPtr = %addr(triggerBuffer) + triggerBuffer.oldOffset ;
newPtr = %addr(triggerBuffer) + triggerBuffer.newOffset ;
D oldFile E DS extName(fileName) qualified
D based(oldPtr)
D newFile E DS extName(fileName) qualified
D based(newPtr)
D oldPtr S *
D newPtr S *
oldPtr = %addr(triggerBuffer) + triggerBuffer.oldOffset ;
newPtr = %addr(triggerBuffer) + triggerBuffer.newOffset ;
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
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 the data structures are qualified to allow for differentiation between field names in the before and after images.
If your records include null capable fields, you can map the null flag array in a similar fashion.
© ComCon and System i Developer, LLC 2006-2011 -28-
ComConRecord Locking
How do you handle record locking in Interactive programs?
Just lock the records without error recovery - A wealth of possible problems. Batch jobs crashing etc.
Lock the records with error recovery - A better solution but do all programs have error recovery?
- Bottom line with error recovery: You ain�t getting the record
Do not lock the records and hope for the best - If you track the fields with have been changed by the current user and output only those
fields you MIGHT be ok. How do you track those changes?
- Your user will not be aware of concurrent changes- If you do not track changes you may overwrite data changed by another user with older
screen information.
Do not lock the records and program for concurrent maintenance- Best solution yet!
© ComCon and System i Developer, LLC 2006-2011 -29-
ComConLogic for Record Locking
This technique allows for
Same field names on display file and database file
Concurrent maintenance
FmyFile UF A E K Disk
FmyScreen CF E WorkStn
D IOData E Ds extName(myFile)
D recordImage Ds likeRec(myFileR) dim(2)
chain(N) key myFile;
recordImage(1) = IOData;
exFmt screen;
chain key myFile recordImage(2);
if recordImage(1) = recordImage(2); // Or compare individual fields
update myFileR;
else;
unLock myFile; // And do any error handling
endIf;
FmyFile UF A E K Disk
FmyScreen CF E WorkStn
D IOData E Ds extName(myFile)
D recordImage Ds likeRec(myFileR) dim(2)
chain(N) key myFile;
recordImage(1) = IOData;
exFmt screen;
chain key myFile recordImage(2);
if recordImage(1) = recordImage(2); // Or compare individual fields
update myFileR;
else;
unLock myFile; // And do any error handling
endIf;
Same field namesSame field names
The basic logic is as follows:
� chain(N) key myFile; - the initial row is retrieved. It is automatically read into the IOData DS
� recordImage(1) = IOData; - store the original image for later comparison
� exFmt screen; display and read the screen. Data in IOData will be overwritten with data from the screen
� chain key myFile recordImage(2); get the record again. But this time lock the record and specify a target for the read so the data in IOData is not overwritten
� if recordImage(1) = recordImage(2); check that the row has not been changed since the program originally retrieved it. This can be a comple row comparison, individual columns etc.
� update myFileR; or unLock myFile; depending on the results of the comparison
© ComCon and System i Developer, LLC 2006-2011 -30-
ComConExample of Dynamic Memory Allocation
This example shows the use of dynamic memory allocation for an array that may have more then 32767 elements
The array is �repositioned� every 32767 elements
D dataArray S * dim(32767)
D based(pDataArray)
// Allocate memory for the array of pointers
pDataArray = %alloc((%size(pDataPtr) * numberInList));
// Build the array of pointers to the path entries
for i = 1 to numberInList;
j = j + 1;
if (j > %elem(dataArray));
pDataArray = pDataArray +
(%Size(dataArray) * %Elem(dataArray));
j = 1;
endIf;
dataArray(j) = pDataPtr;
D dataArray S * dim(32767)
D based(pDataArray)
// Allocate memory for the array of pointers
pDataArray = %alloc((%size(pDataPtr) * numberInList));
// Build the array of pointers to the path entries
for i = 1 to numberInList;
j = j + 1;
if (j > %elem(dataArray));
pDataArray = pDataArray +
(%Size(dataArray) * %Elem(dataArray));
j = 1;
endIf;
dataArray(j) = pDataPtr;
© ComCon and System i Developer, LLC 2006-2011 -31-
ComConOr Use a User Space
This example shows an array being mapped to a User Space
D getSpace PR extPgm('QUSPTRUS')
D spaceName 20 const
D pSpacePtr *
D errorCode const Options(*NOPASS)
D like(standardAPIError)
D dataArray S * dim(32767)
D based(pDataArray)
// Map the array to the User Space
getSpace(SpaceName:pDataArray);
// Build the array
for i = 1 to numberInList;
j = j + 1;
if (j > %Elem(dataArray));
pDataArray = pDataArray +
(%Size(Data) * %Elem(dataArray));
j = 1;
endIf;
dataArray(j) = data;
D getSpace PR extPgm('QUSPTRUS')
D spaceName 20 const
D pSpacePtr *
D errorCode const Options(*NOPASS)
D like(standardAPIError)
D dataArray S * dim(32767)
D based(pDataArray)
// Map the array to the User Space
getSpace(SpaceName:pDataArray);
// Build the array
for i = 1 to numberInList;
j = j + 1;
if (j > %Elem(dataArray));
pDataArray = pDataArray +
(%Size(Data) * %Elem(dataArray));
j = 1;
endIf;
dataArray(j) = data;
Of course the prototype for QUSPTRUS would be in a copy member.
The user space must exist (see the QUSCRTUS API) before calling QUSPTRUS.
© ComCon and System i Developer, LLC 2006-2011 -32-
ComCon
%alloc assigns 1,120,000 bytes
2. pDataArray= %alloc((%size(dataArray)*numberInList));
< 1 ------------------------------------------------------- 1,120,000 >
< 1 ---------------- 524,272 > < 524,273 ------- 1,048,544 >
dataArraydataArray
�Walking� a Large Array
1. numberInList = 70,000
%Size(dataArray) = 16
dataArraydataArray
pDataArray = address of allocated memory
3. Program loops through 32767 elements of dataArray
4. �Move� the position of the array pDataArray = pDataArray + (%Size(dataArray) * %elem(dataArray));
5. etc.
< 1,048,545 --------- 1,572,816 >
ComConUsing Large Arrays
But what about sorting and scanning large arrays?
Use the C functions qsort and bsearch as more powerful versions of SORTA and LOOKUP
Refer to the Red Book "Who Knew You Could Do That With RPG IV?" for details and examples.
D sortIt PR extProc('qsort')
D dataStart * value
D elements 10u 0 value
D size 10u 0 value
D function * procPtr value
D findIt PR * extProc('bsearch')
D lookFor * value
D dataStart * value
D elements 10u 0 value
D size 10u 0 value
D function * procPtr value
D sortIt PR extProc('qsort')
D dataStart * value
D elements 10u 0 value
D size 10u 0 value
D function * procPtr value
D findIt PR * extProc('bsearch')
D lookFor * value
D dataStart * value
D elements 10u 0 value
D size 10u 0 value
D function * procPtr value
© ComCon and System i Developer, LLC 2006-2011 -33-
ComConCheck Out Check Constraints
See the articles
"The Ins and Outs of Using Constraints" (http://search400.techtarget.com/tip/1,289483,sid3_gci1032813,00.html) and
"Name That Constraint" (http://search400.techtarget.com/tip/1,289483,sid3_gci1004244,00.html)
© ComCon and System i Developer, LLC 2006-2011 -34-
ComConSummary
There you have it!
I hope you find a few of the tips and techniques useful and applicable
ComConBy the Speaker
�Re-Engineering RPG Legacy Applications�
ISBN 1-58347-006-9
�The Programmers Guide to iSeries Navigator�
ISBN 1-58347-047-6
www.mcpressonline.com
www.midrange.com
www.amazon.com
etc.
iSeries Navigator for Programmers
A self teach course
www.lab400.com
Article links at
www.comconadvisor.com
© ComCon and System i Developer, LLC 2006-2011 -35-