Tip

Qualified data structures: Why you should be using them


Paul Tuohy

Recently, I have been receiving quite a few questions about qualified data structures and whether or not they are of much use. They are and you should be using them, let me tell you why.

V5R1 of OS/400 saw the introduction of qualified data structures and the LIKEDS keyword in RPG IV. At first glance these appeared to be "neat" enhancements but not something to get too excited about. But, when you start to use them you discover that qualified data structures have a lot more to offer then just a "beefed up" means of prefixing field names. They allow you to use the same field name in multiple structures without name conflict, provide a means of defining standard structures (the most obvious use of this is for APIs), and they lead to self documenting code.

More Information

Qualified data structures continued to be enhanced since V5R1 with the introduction of nested data structures, data structure arrays and the EVAL-CORR (Assign corresponding subfields) operation.

Let's look at the definition of qualified data structures and some of their uses.

The basics (V5R1)

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 (see "Compiler directives -- reap the benefits").

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 

I am not advocating that you should use multi dimensional arrays (they are not the easiest code to read) but it is as well to be aware that they are there if you need them.

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)

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.

It's a wrap

There you have it. Qualified data structures introduce a whole new set of features to RPG IV and, as you continue to enhance and modernize your applications, you will find them to be an invaluable tool. You will soon be wondering how you managed to survive without them!

-----------------------------------
About the author: Paul Tuohy is CEO of ComCon, an iSeries consulting company. He 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 is also an award winning speaker who speaks regularly at US Common conferences and the renowned RPG World conferences.

This was first published in August 2006

There are Comments. Add yours.

 
TIP: Want to include a code block in your comment? Use <pre> or <code> tags around the desired text. Ex: <code>insert code</code>

REGISTER or login:

Forgot Password?
By submitting you agree to receive email from TechTarget and its partners. If you reside outside of the United States, you consent to having your personal data transferred to and processed in the United States. Privacy
Sort by: OldestNewest

Forgot Password?

No problem! Submit your e-mail address below. We'll send you an email containing your password.

Your password has been sent to:

Disclaimer: Our Tips Exchange is a forum for you to share technical advice and expertise with your peers and to learn from other enterprise IT professionals. TechTarget provides the infrastructure to facilitate this sharing of information. However, we cannot guarantee the accuracy or validity of the material submitted. You agree that your use of the Ask The Expert services and your reliance on any questions, answers, information or other materials received through this Web site is at your own risk.