Figure 1
shows the definition and usage of a qualified data structure. The data structure
contains the components of a phone number. The use of the QUALIFIED keyword on the DS line identifies the
data structure as being qualified. This means that all references to the
subfields in the data structure must be qualified with the data structure name
using a dot notation (data structure name dot field name – DSNAME.SUBFIELD).
The subfields in the qualified data structure may not be referenced by their
field name alone.
D Phone DS Qualified
D CountryCode 5 Varying
D NDDPrefix 5 Varying
D AreaCode 5 Varying
D Number 9 Varying
D Extension 4 Varying
D IDDPrefix 5 Varying Dim(5)
If Phone.CountryCode <> '353' ;
DialNumber = Phone.IDDPrefix(1) + Phone.CountryCode;
Else;
DialNumber = Phone.NDDPrefix;
EndIf;
DialNumber = DialNumber + ' ' + Phone.AreaCode + Phone.Number;
Figure 1: Definition and use of a qualified data structure
Qualified data structures mean that you
can now have the same field name in multiple data structures. It is even
possible to have different definitions (data type, length) for a field in
different data structures (possible but not desirable).
The LIKEDS keyword allows you to define
a data structure like another data structure; just as the LIKE keyword allows
you to define a field like another field. The example in Figure 2 shows the
definition of three data structures for phone numbers, each one being like the
phone data structure. Data
structures that are defined using LIKEDS are implicitly qualified (even if the
parent data structure is not qualified) and do not need the QUALIFED keyword. Each data structure has the same subfields but the data structure
name is used as a qualifier to identify which one you are using.
D Phone DS Qualified
D CountryCode 5 Varying
D NDDPrefix 5 Varying
D AreaCode 5 Varying
D Number 9 Varying
D Extension 4 Varying
D IDDPrefix 5 Varying Dim(5)
D HomePhone DS LikeDS(Phone)
D CellPhone DS LikeDS(Phone)
D WorkPhone DS LikeDS(Phone)
If HomePhone.CountryCode <> '353' ;
DialNumber = HomePhone.IDDPrefix(1) + HomePhone.CountryCode;
Else;
DialNumber = HomePhone.NDDPrefix;
EndIf;
DialNumber = DialNumber + ' ' + HomePhone.AreaCode + HomePhone.Number;
Figure 2: Using LIKEDS to define data structures like a data structure
Figure 2 also demonstrates the self
documentation features of qualified data structures. When you see a subfield
used in the calculation code you are in no doubts as to where it is coming
from.
You should note that LIKEDS does not
duplicate the initialization of any subfields defined in the parent data
structure but you can achieve this by specifying INZ(*LIKEDS) for the new data
structure.
Using qualified data structures
The LIKEDS keyword provides a means of
defining standard structures that may be used throughout an application. In the
same way as you have a copy member containing all prototypes you may also have
a copy member containing a set of standard definitions .
The initial problem would be that all of
these standard definitions are occupying space in a program when the program is
only using one or two of the definitions.
The key here is that these
"standard definitions" are for definition purposes only; they are
intended only to be used as the parent for a LIKEDS. To ensure that a parent
data structure does not occupy any space in a program you base the data
structure on a pointer which is never assigned a value, as shown in Figure 3;
therefore, the data structure never occupies any space but may still be used as
the basis for a LIKEDS.
D Phone DS Qualified
D Based(Dummy_Ptr)
D CountryCode 5 Varying
D NDDPrefix 5 Varying
D AreaCode 5 Varying
D Number 9 Varying
D Extension 4 Varying
D IDDPrefix 5 Varying Dim(5)
Figure 3: A data structure defined for use as a standard definition
This technique has a lot of uses in your
applications. As you move into the world of Integrated Language Environment
(ILE) and start to use subprocedures more; you will find that you have more
requirements to pass data structures as parameters between subprocedures.
But let's look at using this technique
for something you might be more familiar with -- an information data structure.
Assuming that the sample shown in Figure 4 is coded in a copy member named
BASEFORMAT, the code shown in Figure 5 shows the definition of three files,
each with a file information data structure. LIKEDS is used to define each of
the file information data structures based on the standard definition in
BASEFORMAT.
D Base_InfDS Ds Qualified
D Based(Bummy_Ptr)
D FileName 8
D Open 1
D EOF 1
D Status 5S 0
D OpCode 6
// etc. etc. etc.
Figure 4: A standard definition for a file information data structure
FMyFile1 IF E K Disk InfDs(DS_MyFile1)
FMyFile2 IF E K Disk InfDs(DS_MyFile2)
FMyFile3 IF E K Disk InfDs(DS_MyFile3)
/Copy BaseFormat
D DS_MyFile1 Ds LikeDS(Base_InfDS)
D DS_MyFile2 Ds LikeDS(Base_InfDS)
D DS_MyFile3 Ds LikeDS(Base_InfDS)
Figure 5: Defining file information data structures based on a standard definition
The other obvious use of this technique is
with APIs. How about defining standard definitions for the different formats
used as parameters?
This technique of defining standard
structures for definition purposes is very useful and is one that I use in
nearly every program that I write.
Nested data structures (V5R2)
Qualified data structures also made
possible the introduction of nested data structures which means you can define
a data structure, within a data structure, within a data structure…
D ContactInfo DS Qualified
D HomePhone LikeDS(Phone)
D CellPhone LikeDS(Phone)
D WorkPhone LikeDS(Phone)
If ContactInfo.HomePhone.CountryCode <> '353' ;
DialNumber = ContactInfo.HomePhone.IDDPrefix(1)
+ ContactInfo.HomePhone.CountryCode;
Else;
DialNumber = ContactInfo.HomePhone.NDDPrefix;
EndIf;
DialNumber = DialNumber + ' ' + ContactInfo.HomePhone.AreaCode
+ ContactInfo.HomePhone.Number;
Figure 6 shows an example of the three
phone data structures being defined in a data structure. A fully qualified name
(DSNAME1.DSNAME2.SUBFIELD) must be used to reference any of the subfields.
D ContactInfo DS Qualified
D HomePhone LikeDS(Phone)
D CellPhone LikeDS(Phone)
D WorkPhone LikeDS(Phone)
If ContactInfo.HomePhone.CountryCode <> '353' ;
DialNumber = ContactInfo.HomePhone.IDDPrefix(1)
+ ContactInfo.HomePhone.CountryCode;
Else;
DialNumber = ContactInfo.HomePhone.NDDPrefix;
EndIf;
DialNumber = DialNumber + ' ' + ContactInfo.HomePhone.AreaCode
+ ContactInfo.HomePhone.Number;
Figure 6: Defining nested data structures
Nested data structures may only be
defined in a qualified data structure. Subfields in nested data structures may
only be referenced in free form and extended Factor 2 C specs. The traditional
C spec, I spec and O spec and keyword on F specs and D specs only allow you to
refer subfields in a single level qualified data structure (DSNAME.SUBFIELD).
Data structure arrays (V5R2)
Qualified data structures also led to
data structure arrays -- you need never use a multiple occurrence data
structure again! Defining a data structure as an array is just as it is for a
field, you use the DIM keyword as shown in Figure 7 for the WorkPhone data
structure. The data structure name is indexed as with any array. ContactInfo.WorkPhone(x).CountryCode
refers to the CountryCode field in the xth element of the WorkPhone data
structure array in the data structure ContactInfo. ContactInfo.WorkPhone(x).IDDPrefix(1) refers to the
first element of the IDDPrefix array in the xth element of the WorkPhone data
structure array in the data structure ContactInfo (a multi dimensional array).
D ContactInfo DS Qualified
D HomePhone LikeDS(Phone)
D CellPhone LikeDS(Phone)
D WorkPhone LikeDS(Phone) Dim(5)
If ContactInfo.WorkPhone(x).CountryCode <> '353' ;
DialNumber = ContactInfo.WorkPhone(x).IDDPrefix(1)
+ ContactInfo.WorkPhone(x).CountryCode;
Else;
DialNumber = ContactInfo.WorkPhone(x).NDDPrefix;
EndIf;
DialNumber = DialNumber + ' ' + ContactInfo.WorkPhone(x).AreaCode
+ ContactInfo.WorkPhone(x).Number;
Figure 7: Defining and using data structure arrays
LIKEREC (V5R2)
QUALIFIED and LIKEDS are not the only ways
of defining a qualified data structure. The LIKEREC keyword defines a data
structure like a record format of a file defined on the file specs and, as with
LIKEDS, the data structure is implicitly qualified. Figure 8 shows the
definition of three data structures using the LIKEREC keyword. The first parameter
identifies the MyFileRec record format (i.e. the name of the record format on
the file MyFile). The second parameter identifies which fields are included in
the data structure; *KEY for key fields, *INPUT for input fields and *OUTPUT
for output fields. The ForKey data structure is used as an argument for the
*KDS built in function when chaining to the file and the record is placed in
the ForInput data structure. The data in the ForOutput data structure is
written to the file.
FMyFile UF A E K Disk
D ForKey DS LikeRec(MyFileRec:*Key)
D ForInput DS LikeRec(MyFileRec:*Input)
D ForOutput DS LikeRec(MyFileRec:*Output)
Chain %KDS(ForKey) MyFileRec ForInput;
Write MyFileRec ForOutput;
Figure 8: Using the LIKEREC keyword
Note that the record format name must be
used on all file operations that read a record into or output a record from a
data structure. Also, the *INPUT value must be specified for any data structure
that will be used for an input operation and *OUTPUT must be specified for any
data structure that will be used with an output operation.
EVAL-CORR (V5R4) (Assign corresponding subfields)
And finally, there is the EVAL-CORR
operation. A feature that has long been available in COBOL and now has a role
to play in RPG. The EVAL-CORR
operation copies the contents of fields from one data structure to fields of
the same name and compatible data definition in another data structure. Figure 9 shows an example of using the
EVAL-CORR operation to copy the contents of fields from the DS1 data structure
to the DS2 data structure. The contents of the Name and Balance fields are
copied since they are the same field names and data types (even though the
length of the Name field is longer in Ds2). The content of the Area field is
not copied since it is a packed numeric field in DS1 and a character field in
DS2. Of course, the more fields you have in the data structures the more useful
EVAL-CORR is.
D Ds1 DS Qualified
D Name 30A
D Address1 30A
D Balance 11P 2
D Area 5P 0
D Ds2 DS Qualified
D Balance 13P 2
D Name 35A
D City 30A
D Area 5A
Eval-Corr Ds2 = Ds1;
// This is the same as
// Ds2.Balance = Ds1.Balance;
// Ds2.Name = Ds1.Name;
Figure 9: Using the EVAL-CORR operation
The compiler generates messages
identifying which fields are and are not copied and why.
You may also specify extenders of H, M
and R but they are only available in free format, there is not enough room for
EVAL-CORR and an extender in the 10 character operation code in fixed format or
extended factor 2.
The EVAL-CORR operation assigns data and null-indicators
from the corresponding subfields of the source data structure to the subfields
of the target data structure. The subfields that are assigned are the subfields
that have the same name and compatible data type in both data structures. For
example, if data structure DS1 has character subfields A, B, and C, and data
structure DS2 has character subfields B, C, and D, statement
EVAL-CORR DS1 = DS2
will assign
data from subfields DS2.B and DS2.C to DS1.B and DS1.C. Null-capable subfields
in the target data structure that are affected by the EVAL-CORR operation will also have their null-indicators
set from the null-indicator from the source data structure's subfield, or to
*OFF, if the source subfield is not null-capable.
If an operation code extender H is specified, the half-adjust
function applies on all numeric assignments. Extenders for EVAL-CORR can be specified only in Free-form
calculations.
If operation code extender M or R is specified, it applies to
the arguments of any procedure call specified as part of the source or target
expression. Extenders for EVAL-CORR
can be specified only in Free-form calculations.
The EVAL-CORR Summary section in the compiler
listing can be used to determine
·
which
subfields were selected to be affected by the EVAL-CORR
operation
·
for
subfields not selected, the reason the subfield was not selected
·
for
subfields that are selected, any additional information about the subfields
such as a difference in the dimension or null-capability of the subfields.
See the Rational Development Studio for i: ILE RPG
Programmer's Guide for more information about the EVAL-CORR Summary section.
Remember the
following when using the EVAL-CORR
operation:
·
Operation
code EVAL-CORR may be coded either
in free-form calculations or in fixed-form calculations. When coded in
fixed-form calculations, the assignment expression is coded in the Extended
Factor 2 entry, with the Factor 1 entry left blank.
·
The
source and target operands must both be data structure variables, including
data structure subfields defined with LIKEDS or LIKEREC.
·
The
operands may be qualified or unqualified data structures. However, for the
operation to be successful, at least one of the operands must be a qualified
data structure; otherwise, it would not be possible for the two data structures
to have any subfields with the same name.
·
The
subfields involved in the assignment are those that have the same name in both
data structures and have data types that are compatible for assignment using
EVAL.
·
When
comparing the subfield names to find corresponding subfieds, the names used are
the internal program names; the internal program names may be different from
the external names in the case of fields from externally-described files or
data structures. For fields defined externally and renamed or prefixed, the
name used is the name after applying the rename or prefix.
·
For
subfields in the source and target that correspond by name and are both data
structures defined with LIKEDS or LIKEREC, the subfields that are assigned are
the corresponding subfields of the subfield data structures. If two subfields
in the source and target have the same name but one is a data structure defined
with LIKEDS or LIKEREC, and the other is not a data structure, the subfield is
not assigned by the EVAL-CORR
operation.
·
The
assignment of data from the source subfields to the target subfields follows
the same rules as for operation code EVAL. For example, character values are
assigned left adjusted with truncation or padding with blanks for unequal
lengths.
·
Data
is assigned subfield by subfield by the order of subfields in the source data
structure. If there are overlapping subfields in the target data structure,
either due to overlapping from-and-to positions or due to the OVERLAY keyword,
later assignment may overwrite earlier moves.
·
When
the source and target data structures or corresponding source and target
subfields which are both data structures are defined the same way with LIKEDS
or LIKEREC, that is, both data structures are defined like the same data
structure, the compiler will optimize the assignment and assign the data
structure as a whole, and not as a series of individual subfield assignments.
·
If
either the source or target operand is a multiple occurrence data structure,
the current occurrence is used.
·
If
you are working with arrays:
·
If
the source operand is an unindexed array data structure, the target data
structure must also be an array data structure.
·
If
the target operand is an unindexed array data structure, the operation works on
each element of the array data structure, following the same rules as EVAL with
an array result. %SUBARR may be used to restrict the number of elements used in
either the source or target data structure array.
·
If
one subfield is an array, both subfields must be arrays. If the dimension of
one array subfield is smaller than the other, only the smaller number of array
elements is assigned. If the target subfield has more elements, the additional
elements are unchanged by the EVAL-CORR
operation.
·
If
you are working with null-capable subfields:
·
EVAL-CORR automatically handles assignment of
null-indicators for null-capable subfields that are not data structure
subfields.
·
If
both the source and target subfields are null-capable, the source subfield's
null-indicator is copied to the target subfield's null-indicator.
·
If
the target subfield is null-capable and the source subfield is not
null-capable, the target subfield's null-indicator is set to *OFF.
·
If
the source subfield is null-capable and the target subfield is not
null-capable, the source subfield's null-indicator is ignored.
·
The
EVAL-CORR operation sets the
null-indicators for scalar and array subfields only. If a null-capable subfield
is a data structure, its null-indicator will not be set by the EVAL-CORR operation; similarly, if the target data
structure itself is null-capable, its null-indicator will not be set by the
EVAL-CORR operation..
·
If
the subfield is a data structure and a null-indicator is assigned to the data
structure itself, the null-indicator is not affected by the EVAL-CORR operation.