Last month, we looked at ILE RPG Prototyping Primer Keywords. In this second act, I want to look at two things.
First, let’s finish up with the keywords that we can apply to the prototype subfields. This will primarily be the CONST keyword.
And second, I want to take a closer look at the various cases involving the lengths of those subfields and how the relationship between the different lengths can affect the data being passed.
Editor's Note: This article is excerpted from chapter 16 of 21st Century RPG: /Free, ILE, and MVC, by David Shirey.
The CONST keyword is not a parameter of the OPTIONS keyword, like *OMIT or *VALUES; rather, it is a true keyword in its own right.
It has two different uses, depending on where it is found: in the called or the calling module. In both cases, this keyword is applied to the subfield level of the D-spec, not at the top level, so one field in a PR can be labeled CONST, and the others cannot, and it is OK.
In addition, the CONST keyword can only be used on a prototype D-spec subfield. You can’t apply it to a regular D-spec (or dcl-ds) field.
Using CONST in the Called Program
If you put the keyword on both the PR and the PI D-specs in the called module, then it will prevent the field from being modified in the called program. Remember, those two D-specs must be identical, so you have to put the keyword on the same subfield in both of them.
How does it do that? It causes an error in the compile if the compiler sees the CONST keyword and a logic statement that affects the value of that particular variable in the called program.
You can modify the variable all you want in the calling program. But not in the called program.
Why would you want to do this? How should I know? No telling what you are liable to do when you are on your own. Guess you just want to make sure you or someone else never adds a statement to that called program that affects the parameter value. There is some relationship to speed involved (it’s more efficient to pass CONST fields than regular parms), but seriously, how much processing time can that trim if you have a POWER8 with more computing power than the entire country of Liechtenstein? We’ll talk more about efficiency later.
It’s also possible that, for audit purposes, you might want to ensure that a value can’t be changed. This is how you would do it.
Or you may just view it as a convenient documentation tool—don’t worry about these parms when you look at the program, they can’t change. I can dig it.
In addition to keeping the field from being modified in the called program, it also allows you to pass a literal constant (the system will create a variable to facilitate this) or a function (you know, like a BIF).
The truth is a lot of people use CONST on a pretty regular basis for all the reasons I’ve mentioned, so just be aware of the fact that you might see it.
Oh, and you may actually use it yourself.
Using CONST in the Calling Program
The result of using CONST in the calling program is very different from what we saw in the previous section.
Instead of controlling whether you can or can’t update the parm it is applied to, CONST affects the relationship between the length of a parm in the calling program PR D-spec and the length of it wherever else in the calling program it is defined. Remember, the calling program has a PR but no PI, and the fields listed in the PR are not really defined in the system (although they carry a size and type), so you have to define them elsewhere in the D- or F-specs. Normally, we would expect these two fields to look identical: same type and size. But that is not necessarily the case.
Without using the CONST keyword in the calling program, the field where the parm is defined can actually be larger than the value in the PR D-spec. Yeah, seriously. It has to be the same type, but the length can be larger.
That is, if the length of the field in the PR is 15 and elsewhere in the calling program it is defined as 20, then that is OK; no keyword is required, and no compiler error is generated. The impact on the data in those fields is something else, and we will get to that in a minute.
If, on the other hand, the field in the PR is 15 and elsewhere it is defined as 10, then when you compile this program, you will get an error. You see, the system is always thinking, and it knows that since the length of 10 is the real definition of the field, it will never be able to pass a value with more than 10 characters. And so, it is worried about what will be in those extra five characters of the PR when it is passed but not initialized. Remember, this field doesn’t really exist, and so it is not initialized by default.
So, in this situation, you will get an error, unless—unless you put the CONST keyword on that field in the PR. Then the system would be fine with the fact that the field in the PR is bigger than the field where it is really defined.
Of course, we are still curious what happens to those extra five digits. And the answer is that the data is left-justified and something is filled in on the right-hand side. The question is, what is that “something?”
A Series of Facts About Parameter Size
I want to spend a little more time on parameter size issues before we go any further. And to kick that off, let’s review some facts about the prototype D-specs that you should already know from earlier chapters or a previous life.
Called Program Prototype Facts
The subfields on a PR and PI must be identical—the same size and same data type. Actually, the name can be different, but you have to make sure that you have the name used in the PR defined separately somewhere in the program.
The same is true for any keywords that are applied to those subfields. They must be applied the same to both the PR and the PI.
So far, so good: everything is the same.
Calling Program Prototype Facts
First, remember that even though the calling program only has a PR and no PI, there are still two representations of each PR subfield: the subfield itself on the PR and the real field in the D or F-specs where it is formally defined.
Both of these fields must have the same name and be of the same type (for example, alphanumeric or packed) and name.
But, the two fields (the PR subfield and the defining field) do not have to be the same size. Generally, they will be, but there’s no law forcing it, and so size differences could happen. The question is, what impact does it have? Let’s take a look at a few examples (I won’t promise I have them all covered).
And while I am thinking of it, the keywords on both fields (PR and defining) do not have to be, often are not, and sometimes can’t be, the same.
What Happens If the Field Sizes Do Not Match?
As we go through this exercise, I am going to make one assumption: namely, that the size of a field in the PR of the called program is identical to the size of the field in the PR of the calling program.
That is, I am assuming we have a situation where the PR in both programs is represented by a copybook and so is identical. And if the PR field size in the called program is x characters, then the PI field size in the called program must match it or there would be a compile error. Everybody with me on this? OK, so the only variable is the size of the real field in the calling program relative to the size of the PR field in that program.
Calling Program: Real Field Larger Than PR Field
If the real field is larger than the PR field, then the real field length (not the length in the PR) is the length that will be sent out by the calling program and received by the called program.
This will happen naturally; no special keywords need to be used.
So, if the real field is 10 digits and the PR subfield is 6 digits and we move '123456789012' to that field, then '1234567890' is what will be sent to the called program.
If your field in the called program is 6 digits also (that is, if it matches the PR), then what you will receive into the called program is '123456'.
If your field in the called program matches the calling programs real definition (10 digits), then you will get '1234567890' in the called program.
If your field in the called program is larger than the real field in the calling program, say 12 digits, then you will get the 10 digits from the real field and, because the field in the called program is defined by the PI with two spaces at the end.
As it turns out, if the real field is larger than the PR field, then everything stays very civilized.
Calling Program: Real Field Smaller Than PR Field
If, however, the situation is reversed, if the real field is 6 digits and the PR version of it is 10 and we move '123456789012' to the real field, then we will send out the digits from the real field, '123456'. The question then becomes, what is received on the called program side where we have a 10- digit PR field waiting for it? But, before we get to that, there are two very important caveats we need to point out.
First, you will need to specify either CONST or OPTIONS(*VARSIZE) as the keyword on the PR subfield in the calling program, otherwise the compile of the calling program will fail. That was mentioned earlier, but I was afraid you had forgotten. Or weren’t paying attention. Or maybe I didn’t mention it, I can’t remember.
Second, and this is kind of complicated, so hang on: what is transferred over will depend on which keyword is used: CONST or OPTIONS(*VARSIZE). In both cases, we will only bring over as many real digits as specified in the real field definition regardless of how big the PR field is. The difference lies in how the extra characters between the real field and the PR field length when it arrives at the called program are formatted.
If you specify CONST on the PR subfield, then what will be received at the called program are the number of digits in the real field plus spaces for any digits up to the larger PR field. So, in our example above, CONST would send '123456 ' plus four spaces.
If you specify OPTIONS(*VARSIZE) on the PR subfield, then what will be received at the called program will be the significant digits from the real field plus undefined characters (not spaces) for the length of the PR subfield. Using the same example as above, we would receive '123456????' in at the called program.
The problem I have is deciding which keyword to use. Oh, it’s sooooo hard to choose between them: spaces versus undefined characters. I know I should go for the spaces, but I do love surprises. I am going to let you make the call here. Just be aware of what you are getting yourself into.
All seriousness aside, however, you should not need to worry about this for most normal parameters. You will just set the lengths equal (real field and PR subfield in the calling program) and go on with your life. Where this will come into play is on very large parameters, like if you are passing in several KB or more. Maybe you are screwing around with XML. Although in that case, it can be argued that you deserve what happens to you. Then you might have to define a PR field that is very large to handle a wide variety of occurrences but pass in a varying, potentially smaller, amount. Obviously, if you find yourself in this type of situation, I suggest you do some serious testing.
Options(*VARSIZE) and VARYING
No discussion of subfield keywords would be complete without at least mentioning OPTIONS(*VARSIZE) and VARYING. They are not new, and they are not specifically designed for ILE, so part of me doesn’t want to talk about them at all. But I believe there is a certain amount of confusion related to them, and I feel I deserve a chance to make it worse.
OPTIONS(*VARSIZE) is the older of the two, with VARYING having been added in version 4. Like most IBM things, they are similar and yet different, but basically, they “define” the field to which they are attached as a variable-length field where you can store anything from 0 to the maximum number of digits allowed by the length of that field.
Why would we want to use varying-length fields? For a long time, I assumed it was just a desire on the part of some programmers to be difficult, but there is a real reason for it. Almost every opcode in RPG is optimized to be more efficient when dealing with variable-length fields than fixed-length fields. That sounds almost counterintuitive to me, but they say it’s true, and IBM has never lied to me yet. This is not a big deal if you have a 10-digit customer number that may range from 4 to 10 digits, but if you are dealing with very large fields, say 10K or more, then the efficiency savings can indeed build up.
Plus, and this will sound a little like an X-Files conspiracy thing, but—there seems to be a movement toward using variable fields across the board. I don’t know if it’s because of the efficiency, or if General Ripper was right and it’s in the drinking water, or if it is the very first sign of the coming Zombie Apocalypse, but I see variable-length fields popping up everywhere. Is it possible that I am just a hair paranoid? I actually recently watched Zombieland and Shaun of the Dead, so I am a bit more on guard than usual. (Those are about the scariest movies I can watch. I still have nightmares from having accidentally seen The Ring years ago.)
The difference between the two keywords is this:
VARYING will store the current length of the data being held in the field (that is, the length of the current value, not the max length of the field) in the first two digits on the field. You can then use the %LEN BIF to retrieve this length, so that you know how much data you are actually dealing with.
OPTIONS(*VARSIZE) does not do that; you have to figure it out on your own. There are probably other even more esoteric differences between the two, but I don’t feel it’s my place to point them out. Generally, you will see VARYING more often than OPTIONS(*VARSIZE).
A Quick Review
OK, let’s everyone take a deep breath and get our feet back on the ground. If you remember, this all started with a discussion of prototyping and what it means. So let’s step back and see what ground we have covered.
First, let’s look at the calling program.
We know we will have a PR D-spec with the parms that we are going to send, set up as subfields in the spec.
These fields will also have to be defined somewhere else in the program, be it in another, real D-spec, a file, or whatnot. The PR looks real, but it does not really define a field as an entity.
In the simplest case, the length of a subfield in the PR will be equal to the length in reality.
If the real length of the field is greater than the length specified in the PR subfield, then things will work fine except that the actual data transferred may be truncated when it gets to the called program. But remember that the amount sent will depend on the length of the real field definition, not the length of the subfield in the PR of the calling program.
If the real length of the field is less than the length specified in the PR subfield, then we need to use the CONST keyword on the PR subfield to prevent a problem from occurring. This is the use of the CONST keyword in the calling program.
And then the called program.
We are going to have both a PR and a PI D-spec, and they must match each other in every way (keywords, number/type/length of subfields).
The PI D-spec takes care of defining the parms, so they don’t need to be defined anywhere else in the program.
If the CONST keyword is used on any of the subfields, that field may not be modified within the called program, and any instructions that do so will result in compiler errors.
If the length of a subfield in the PR/PI is less than the length in the calling program, there is no problem.
If the length of a subfield in the PR/PI is greater than in the calling program, you will probably have to use the VARYING keyword on the subfields involved to prevent garbage from being transferred in.