Scott’s Corner – OPTIONS(*CONVERT) Keyword

On December 2, PTFs were released that added some new features to RPG.  One of those features is the OPTIONS(*CONVERT) keyword.  You add this keyword to parameters on your prototypes, and RPG will automatically convert the parameter to a character data type.

For example, consider the following subprocedure:

dcl-proc printVal; dcl-pi *n; value varucs2(25) options(*convert) const; end-pi; dsply value; end-proc;

In this case, I accept a parameter that is a Unicode string and I’ve defined OPTIONS(*CONVERT), To keep things simple, this procedure simply prints the parameter using the DSPLY opcode.

Here’s what makes it interesting:  It doesn’t matter what data type the caller provides!  No matter if it is numeric, character, date or time – RPG will automatically convert it to VARUCS2(25), since that is what I specified for this parameter.

For example, the following will work fine:

dcl-s ts timestamp inz(*sys); dcl-s dt date(*dmy) inz(*sys); printVal(%time()); printVal(%date()); printVal(ts); printVal(dt); printVal(d'2023-01-01');

The %TIME and %DATE built-in functions retrieve the current system time and date.  Even though printVal() specifies a character parameter, the system will automatically convert them to character strings before passing them.

Likewise, the variables ts and dt represent timestamp and date fields, respectively.  The final example uses a date literal.  It doesn’t matter, they will all be converted to character, and the output looks like this:

DSPLY 05.33.06 DSPLY 2022-12-13 DSPLY 2022-12-13- DSPLY 13/12/22 DSPLY 2023-01-01

Notice that the date that specified DATFMT(*DMY) printed in day-month-year format, the others did not have a format specified, so printed in the format of their respective data type.  As a rule-of-thumb, it converts the data the same way that the %CHAR built-in function would.

I can also pass character and numeric fields, constants and literals to my printVal() subprocedure.

dcl-s fl float(8) inz(10.1); dcl-s zn zoned(19:5) inz(123.45); dcl-s pk packed(19:5) inz(45.123); printVal('Hello World'); printVal(1234); printVal(fl); printVal(zn); printVal(pk);

And it will convert them to the appropriate character value and display them.

DSPLY Hello World DSPLY 1234 DSPLY +1.010000000000000E+001 DSPLY 123.45000 DSPLY 45.12300

There are some restrictions with this feature.

  • The parameter data type must either be a form of character (char, varchar, ucs2 or varucs2) or a pointer.
  • The parameter must be defined as CONST or VALUE.
  • If a pointer is used, it will be treated the same as options(*string), except that the caller can pass non-character data types.
  • While the parameter needs to be character, the caller can be just about anything. The only types it will not convert are pointers, procedure pointers and Java objects.

I suspect this feature will be useful for making subprocedures more versatile and easier to call.  Since the caller can provide any data type, it won’t matter as much what format the caller’s data is in.

In a more extreme case, you could use it to add functionality to your subprocedure that adapts to the data that was given.  For example, if I want to retrieve a customer’s phone number from the customer master (CUSTMAS) file, I can pass a search string with options(*convert) as follows:

dcl-proc getPhone; dcl-pi *n varchar(20); cust varchar(30) options(*convert) const; end-pi; dcl-s custno packed(5: 0); dcl-s isNumber ind inz(*off); dcl-s phone varchar(20); monitor; custno = %dec(cust:5:0); isNumber = *on; on-error; endmon; if isNumber; exec SQL select phone into :phone from CUSTMAS where cust = :custno; else; exec SQL select phone into :phone from CUSTMAS where name like '%' concat trim(:cust) concat '%'; endif; if %subst(sqlstt:1:2) in %LIST('00': '01'); return phone; else; return '** NOT FOUND **'; endif; end-proc;

Notice that I have an indicator called isNumber that will be on if the parameter can be interpreted as a valid number.

If a number was passed, I search the CUSTMAS file by the customer, but if it wasn’t a valid number, I used SQL to search for customer names that contain the character string provided.

For example, I can do this:

 snd-msg getPhone(1001); snd-msg getPhone(9999); snd-msg getPhone('Scott'); snd-msg getPhone('Tracy');

In this example, the first two calls will retrieve the customer number for account 1001 or 9999, respectively. The next ones will search for a customer with ‘Scott’ in their name, or ‘Tracy’ in their name.  If a matching row Is found, their phone numbers will be printed (using the SND-MSG opcode that was added back in spring.)

I look forward to seeing all of the creative and useful things that people find to do with this new feature!

Scott Klement
Midrange Dynamics Development & Solutions Architect

Scott Klement is an IT professional with a passion for both programming and mentoring. He joined Midrange Dynamics at the beginning of October 2022. He formerly was the Director of Product Development and Support at Profound Logic and the IT Manager and Senior Programmer at Klement’s Sausage Co., Inc. Scott also serves on the Board of Directors of COMMON, where he represents the Education, Innovation, and Certification teams. He is an IBM Champion for Power Systems.

Subscribe to our newsletter and join us next month to see what is happening in Scott’s Corner. Add a great dad joke to your arsenal and gain an even better IT insight from this recognized industry expert as he continues his quest to educate and support the IBM i community.