Morphing parameters, C language-like strings, and (sort of) self-formatting parameters—hidden or at least little know features provided by the OPTIONS keyword. Curious? Continue reading to find out!
This is the final article of the options series. If you missed parts 1 and 2, look here and here. And now let's move on!
The Morphing Parameter (Wait, What?!)
The idea of a "morphing parameter" that can change size at each call can be hard to wrap your head around, but the OPTIONS(*VARSIZE) option is extremely useful, as you'll soon see.
If you ever used one of the many APIs IBM provides, you've probably come across the *VARSIZE option. One classic example is the QCMDEXC API, which allows you to run CL commands from within an RPG program:
D QCMDEXC PR ExtPgm('QCMDEXC')
D P_CmdString 32702A Const OPTIONS(*VARSIZE)
D P_CmdLen 15P 5 Const
D P_DbcsFlag 3A Const OPTIONS(*NOPASS)
And if you've read the previous articles of this series, most of what you see here should be familiar: you'll see that I'm defining a prototype in order to use the QCMDEXC API as if it were a procedure or function; then, you'll find that all the parameters use the CONST keyword and the last parameter P_DbcsFlag is optional, via the OPTIONS(*NOPASS) keyword. The only part that is truly new is the use of the OPTIONS(*VARSIZE) in the P_CmdString parameter. Speaking of which, have you noticed the unusually large size of the parameter? A parameter that long can cause some performance issues, even with the CONST keyword, because it requires a sizable chunk of memory to be allocated every time the API is called. That's why the *VARSIZE option is being used here. This option removes the matching parameter size restriction and allows you to pass a variable of any length, up to the size of the parameter, without causing compilation errors (I'll address run-time errors in a second). In practice, this means that you can call QCMDEXC using this:
D QCMDEXC PR ExtPgm('QCMDEXC')
D P_Cmd 350A
D P_Cmdlen 15P 5
P_Cmd doesn't have the 32.702 characters that QCMDEXC requires, but since it makes use of the *VARSIZE option, a variable of any length up to 32.702 works. However (and this is really, really important), you need to "clean" the parameter value before using it. Since you're not using the full length of the parameter, the part that is not being used may (OK, probably will) contain "garbage"—memory that was used for something else, which has nothing to do with your variable. How do you do it? If you answered "with a BIF," then you're (almost) correct.
Just let me do a quick recap here: to make sure that it was safe to use a parameter defined with OPTIONS(*NOPASS), you'd resort to %PARMS; and to check if a parameter was not omitted when defined with OPTIONS(*OMIT), you'd use %ADDR. So it's only logical that the "cleaning" process I mentioned earlier uses BIFs. In this case, it uses two: %TRIM (which I'll explain in detail in the next article) and %LEN. This last BIF returns the number of digits or characters of a variable expression. However, in this particular case, if you use %LEN by itself, you'll get the field length (32.702), which is not what you want. What you want is the length of the content of the variable, not the length of variable itself. That's where %TRIM comes in: this BIF removes the leading and trailing blanks of a string. By using a combination of these two BIFs, you're able to determine the length you need to "clean" the *VARSIZE-defined parameter of any garbage. You do that by using a work variable and the %LEN-%TRIM combination and the %SUBST (substring) BIFs to retrieve the valid content of the parameter:
* Retrieving the valid content of P_Cmd
C Eval W_Cmd = %SUBST(P_Cmd : 1 :
C %LEN(%TRIM(P_Cmd)))
C-Like Strings
In the C language (and in a few other languages too), the strings are null-terminated. This means that a special character (x'00') is inserted after the last character of the string. So the next option on my list, *STRING, is basically used to invoke C APIs, even though you can use it in "standard" RPG programs as well, because RPG supports both types of strings: the ones we're used to (the fixed-length strings) and the null-terminated ones. Since null-terminated strings are usually related to pointers and that's a fairly advanced topic, I won't explain it in detail here. If you want to see an example of the OPTIONS(*STRING) keyword in action, read this post about a QCMDEXC alternative. The System() function has the same functionality as QCMDEXC, but it doesn't need to know the length of the command, because the OPTIONS(*STRING) keyword will cause the command string to be concatenated with the x'00' special character, create a temporary variable with it, and pass a pointer to that variable to the procedure.
(Sort of) Self-Formatting Parameters
Finally, a few words about the *RIGHTADJ and *TRIM options. I could never really find a practical use for them, but let me explain what they do. Their names tell the whole story.
Let's start with *RIGHTADJ. The value passed to a *RIGHTADJ-defined parameter will get (you guessed it) right-adjusted once the procedure/function begins to execute, assuming that the content is shorter than the parameter.
As for the *TRIM option, it also does what the name suggests: the passed parameter is copied without leading and trailing blanks to a temporary variable. If the parameter is not a varying-length parameter, the trimmed value is padded with blanks (on the left if OPTIONS(*RIGHTADJ) is specified; otherwise on the right). Then the temporary variable is passed (instead of the original parameter) to the called procedure/function. Specifying OPTIONS(*TRIM) causes the parameter to be treated exactly as though %TRIM were coded on every call to the procedure.
There's one more option, named *NULLIND, which I won't discuss here, because its use requires some additional keywords and is somewhat an advanced feature that might not be easy to grasp for those taking the first steps in ILE. If you really want to know, just Google "OPTIONS(*NULLIND)" to find links that might help.
Now You Know Your Options
Well, that ends this "Do You Know Your Options?" series. The next articles will be about built-in functions (BIFs) and their many uses. Until then, feel free to comment about, correct, and improve on this article in the comments section below or in one of the LinkedIn group discussions where my articles usually pop up!
LATEST COMMENTS
MC Press Online