(Editor's note: Bob is on vacation this week, so he offers this "best of" article in case you missed it the first time.)
When OS/400 V5R1 was announced, it included what I would consider the most significant enhancement to RPG IV data structure support in history: qualified names.
Qualified data structure names allow you to refer to the subfields of a data structure by qualifying them to the data structure name itself. This means that within the calculation specifications (or C specs), you refer to a subfield name by typing the name of the data structure, then a period, and then the subfield name, something like this:
Eval MyStruct.MySubfield = 'ABCDEFG'
The period qualifies the data structure subfield named MYSUBFIELD to its parent data structure, named MYSTRUCT.
Qualified data structures provide a unique benefit; you may use the same subfield name in more than one data structure. And the names do not have to have the same attributes (as you might think). They are distinct fields. In fact, when a data structure is a qualified data structure, the subfield names must be referred to using the qualified syntax only.
To identify a data structure as a qualified data structure, the new QUALIFIED keyword is used. Specify this keyword on a data structure definition, and it becomes a qualified data structure.
If you use the QUALIFIED keyword, you may only refer to the subfields in that data structure by using qualified syntax. This is what allows duplicate field names to be assigned. Since the subfields of a qualified data structure must be qualified when they are used, the subfields are scoped to the data structure. For example, the three data structures listed in Figure 1 are perfectly valid in RPG IV.
Figure 1: Here are three examples of qualified data structure
The first data structure, CUSTOMER, is a qualified data structure, so the subfields of that data structure may only be referred to by using the qualified syntax. This also allows the subfield names to be reused elsewhere in the source member, which is exactly what I did in the second data structure, UNDO_BUFFER. This data structure is a multiple-occurrence data structure and is qualified. Note that it contains the same set of subfield names as the CUSTOMER data structure. This data structure might be used to provide some level of "undo" capability in an application. Simply move CUSTOMER to an occurrence of UNDO_BUFFER and then increment the UNDO_BUFFER's occurrence. Again, since the UNDO_BUFFER is qualified, each subfield must be referred using the qualified syntax. The following are valid references to subfields in the CUSTOMER data structure:
The following are valid references to the subfields of the UNDO_BUFFER data structure:
The QUALIFIED keyword finally gives us the ability to use the same subfield name in more than one data structure. This is because the data structure is qualified and the subfields may only be referred to using qualified syntax. Because of this, the compiler can distinguish between the subfield names from different data structures. That is, CUSTOMER.CUSTNO is different from UNDO_BUFFER.CUSTNO.
But what about the third data structure listed in Figure 1? SAVECUST is a traditional data structure that does not contain the QUALIFIED keyword, so its subfields cannot be referred to using the qualified syntax. They have to be referenced in the traditional method by just specifying the subfield name itself.
If a source member were to contain all three data structures from Figure 1, there would be three definitions for the various subfields. Of course, this is due to the fact that, for example purposes only, I used the same subfield names in all three data structures. In the source member, you would have three definitions for the CUSTNO field, as follows:
- Customer.Custno Packed(7,0)
- Undo_Buffer.Custno Packed(7,0)
- CustNo Packed(5,0)
Each subfield is individual; one has absolutely nothing to do with one another. The QUALFIED keyword gives us the ability to reuse field names. The name of any subfield of a qualified data structure may be use elsewhere in the source member without consequences. Of course, it is up to you to keep things organized.
Data Structure Templates
It might seem like qualified data structures would be enough of an enhancement, but IBM didn't stop there. IBM also provided us with the ability to create templates. Templates are data structures that are used like format-file objects, but rather than create record formats with them, you can declare other data structures by using the format of an existing data structure.
You can use the new LIKEDS keyword to declare a data structure with the same subfields as an existing data structure. Normally, this would create a conflict with subfield names, but when the LIKEDS keyword is used, the QUALIFIED keyword is implied.
Other languages have had this capability for decades. RPG IV joins the rest of the programming language community with its first step toward user-defined data types.
By declaring a data structure, you create a structure whose content is based on the needs at hand. Formerly, it was difficult in RPG to reuse the structure without creating unique subfield names. The PREFIX keyword offered some relief in that each subfield could be renamed by adding a specified prefix set of characters to the field name. Figure 2 shows an example.
Figure 2: The PREFIX keyword offers another way to use externally described data structures.
This example would declare two data structures with similar formats. I say "similar" because, while the subfield definitions would be identical in each structure, the subfield names themselves would be different. The first data structure, CUSTOMER, would have its subfield names prefixed with CM_, and the SAVECUST data structure would have its subfield names prefixed with SAVE_. In Figure 3, these data structures are expanded to illustrate their subfield names.
Figure 3: Here's an example of expanded externally described data structures with PREFIX keyword.
While this is similar to qualified data structures, it is not the same. With the LIKEDS keyword, you can avoid the PREFIX keyword altogether, as follows in Figure 4:
Customer E DS Extname(CustMast)
D SaveCust DS LIKEDS(CUSTOMER)
Figure 4: With the LIKEDS keyword, you don't need the PREFIX keyword at
In this case, the CUSTOMER data structure is still externally described, but it does not include the PREFIX keyword, so its subfield names will be the same as those in the CUSTMAST database file. The format of the SAVECUST data structure is based on the CUSTOMER data structure. The SAVECUST data structure contains the LIKEDS keyword but does not contain the PREFIX keyword. It will have the same subfield names as the CUSTOMER data structure. This is possible because LIKEDS implies QUALIFIED. In fact, you can't even hard code the QUALIFIED keyword when LIKEDS is present; it is redundant, and the compiler will complain.
LIKEDS can be used to create one data structure like another. In using it, you may clone a data structure. There an enhanced way to use LIKEDS--with a template. The word template is loosely applied to a method of declaring a data structure and then basing that data structure on a pointer. When a data structure--or any field for that matter--is based on a pointer, no storage is allocated for the variable. So a data structure that contains the BASED keyword uses no physical storage at runtime. Instead, it uses the storage at the address assigned to its based pointer.
In the context of the LIKEDS keyword, a data structure could be declared and then include the BASED(@) keyword. The "at" symbol (@) is customarily used when you do not assign a value to a pointer--that is, when you are declaring a template, not a structure for storing data. Think of it as declaring an object in your RPG program that's like a format file.
For example, in Figure 5, a data structure named TP_CUSTOMER is declared. It contains the BASED(@) keyword. This causes no storage to be declared for the TP_CUSTOMER data structure, so it doesn't consume any valuable resources in your program.
Figure 5: Declare a data structure as a template.
The letters tp and the underscore in front of the data structure name are a convention that I used for this article; they are not standards, nor are they in any way required. To use this structure in your code, you would declare another data structure and use the LIKEDS keyword, as follows:
The CUSTOMER data structure will contain all the subfields from the tp_CUSTOMER data structure and also will contain the QUALIFIED keyword. No other attributes are inherited. That is, the BASED keyword is not applied to the CUSTOMER data structure. In addition, the LIKEDS keyword does not inherit the initial values of the based data structure. So, if CUSTOMER should be initialized, you will have to initialize it using one of the following methods:
- Specify the INZ keyword on the CUSTOMER data structure definition.
- Use the CLEAR operation code in the calculation specifications, such as in the *INZSR subroutine.
There is a problem when using the BASED keyword in this context. The subfields of the original data structure (the one containing the BASED keyword) may not include the INZ keyword. If a data structure is based, it contains no storage, so no storage can be initialized. Therefore, INZ is not permitted on the based-on data structure. Consequently, creating a storageless template will only work in some circumstances.
There are actually three choices for initializing a derived data structure, however. The third choice is a new option on the INZ keyword. The *LIKEDS option of the INZ keyword can be used to initialize a data structure like the original data structure. The original data structure's initial values may be inherited by using the INZ(*LIKEDS) keyword on the derived data structure's declaration. Figure 6 illustrates this concept.
Figure 6: Another way to initialize a derived data structure is to use LIKEDS with INZ(*LIKEDS).
In Figure 6, the data structure QUALNAME is a traditional RPG data structure. It contains two subfields, OBJECT and LIBRARY. OBJECT is initialized to blank, and LIBRARY is initialized to '*CURLIB'.
The data structure named FILENAME is declared with the LIKEDS(QUALNAME) keyword. The FILENAME will contain both the OBJECT and the LIBRARY subfields. In order to cause the derived data structure to have the same initial values as the original data structure, the INZ(*LIKEDS) keyword is used. This causes FILENAME to inherit the initial values of the QUALNAME data structure.
Most other values of the original data structure are not inherited when the LIKEDS keyword is used, so there's more to duplicating a data structure than just adding the LIKEDS keyword. Listed in Figure 7 are an original data structure and a derived data structure. Note the added keywords needed to fully duplicate the original data structure's attributes.
Figure 7: Duplicate a data structure and its attributes.
The LIKEDS keyword and the INZ(*LIKEDS) option along with the QUALIFIED keyword introduce a level of simplicity in coding data structures that has been long overdue. Before you implement qualified data structures, remember the following:
- These enhancements are in V5R1 and later.
- When the QUALIFIED keyword is used, the subfields of the data structure must be referenced using qualified syntax (e.g., dsname.subfield).
- The LIKEDS keyword may be used to declare one data structure like another. The new data structure is referred to as a derived data structure; the original data structure is referred to as the based on data structure or the parent data structure.
- When the LIKEDS keyword is used, the QUALIFIED keyword is implied.
- Use the *LIKEDS option of the INZ keyword in conjunction with the LIKEDS keyword to inherit the initialization attributes of the parent data structure.
Bob Cozzi is author of the best-selling The Modern RPG IV Language, Fourth Edition as well as RPG TNT: 101 Dynamite Tips 'n Techniques with RPG IV and is host of the i5 Podcast Network, which provides free video and audio podcasts to the i5 community. You can also see him in person at RPG World in May 2007.