%PASSED and %OMITTED Built-in Functions
Joke of the Month
A woman walks into a bar with a lizard on her shoulder, has a seat at a table and waits patiently for the waiter. When the waiter arrives, he says “Welcome! What an interesting pet! What is his name?” The woman responds “Tiny.” The waiter, surprised, says “That’s an unusual name for a lizard. Why do you call him that?” She replies “because… he’s my newt!” (Minute. Get it? Bahahahaha.)
Thoughts of the Month
This has been a busy time for me. I have been in Delavan WI, Denver CO, Westborough MA, Long Island NY, and am about to go to Prague! These are all IBM i events, chock full of great tips, techniques and content for professionals in our community. I’ve also met many of you, and hope to keep meeting more.
If you haven’t been to an in-person event, or it’s been awhile, I highly recommend it. The energy is amazing, the education is life changing. Hope to see you there!
Tip of the month
In April IBM announced the next set of updates to the ILE RPG compiler. One of these new enhancements is support for checking optional parameters with the %PASSED and %OMITTED built-in functions. This will be made available to people running IBM i 7.4+ via PTFs, though at the time that I’m writing this, the PTFs have not yet been released.
Since prototypes were first released back in V3R2, we’ve had two different ways to make parameters optional, these are known as *NOPASS and *OMIT. Each has pros and cons, and prior to the %PASSED built-in function, it required significant understanding of how parameters worked in order to make sure that they were coded correctly. To illustrate this, let’s look at an example of a subprocedure:
In this example, the DoSomething subprocedure accepts 3 parameters. The first one, Param1, has options(*omit) defined – which means that when you call the procedure, you can either pass a value, or you can pass the *OMIT special value. For example:
This is very helpful when a subprocedure has a “default” value. You can pass *OMIT, and the code inside of the procedure can use a default value if it determines that *OMIT was provided. But there are some key things that you should consider when using Param1:
- If you call the subprocedure from RPG and use the prototype, you must always pass something for this parameter (even if it’s the special value *OMIT).
- When you add a new parameter to an existing subprocedure, if *OMIT is used, you must update all of the existing callers to pass *OMIT.
- Any callers that don’t use the prototype (such as those written in other languages like CL, Cobol or SQL) will not be forced by their compiler to provide *OMIT or a value, and unpredictable results can occur checking %addr(Param1) if nothing was passed.
The third parameter, Param3, uses the alternative of *NOPASS, which means it’s possible to call the procedure without passing this parameter at all. For example, you can call it like this:
DoSomething(Var1: Var2: Var3);
Since you can choose to leave the third parameter unpassed, it is important that the code only use this parameter if it was provided. Prior to the %PASSED BIF, the way you’d check that is by checking %PARMS to see how many parameters were passed, and if there’s at least 3 (in this example) then you know Param3 was provided.
- Unlike *OMIT, if this parameter was added to an existing subprocedure, the existing callers do not need to be updated at all.
- Since the parameter is new, the existing callers will already be passing fewer parameters, so the check of %PARMS will still work without changes.
- Callers that do not use the prototype (such as those in other languages) will also work correctly provided that they at least provide the parameter count, so in that respect, it is less error-prone than using *OMIT.
- Callers that don’t use the prototype may still provide *OMIT, but the subprocedure isn’t expecting this. This error is predictable, since the address of the parameter will be null, you will get a null pointer error.
- The procedure must be very careful to never use this parameter (this includes passing it forward to any other procedures or programs) or unpredictable results will occur.
- If a 4th parameter is added to the subprocedure, it must also be *NOPASS, and there’s no way to pass the 4th parameter without also passing the 3rd.
Due to these disadvantages, many programmers choose the “best of both worlds” by allowing both *OMIT and *NOPASS like Param2 does. It can be called like this:
DoSomething(Var1: *OMIT: Var3);
This is easily the most flexible option. But doing it this way, you must be careful to check both %PARMS and the %ADDR of the parameter. If you don’t (or don’t check them correctly) unpredictable results will occur.
The %PASSED Built-in Function
The new %PASSED built-in function aims to simplify parameter handling by automatically checking both the number of parameters passed and whether a parameter was omitted for you automatically. This simplifies coding by making sure it is passed correctly. Using the new %PASSED BIF, we can code our subprocedure as follows:
With %PASSED, the RPG compiler correctly checks both %PARMS and %ADDR under-the-covers. This way, you don’t have to worry about whether the parameter was left off completely, or whether *OMIT was passed, the compiler takes care of that for you. If a caller is not using the prototype and leaves the parameter off, or passes *OMIT when not expected, it will still be handled correctly. As a result, this approach makes the code simpler, but also makes it safer. The new feature doesn’t give us any new capabilities (as long as we checked %PARMS and %ADDR properly, we could already do the same thing) but it much easier for us to code it properly.
The %OMITTED Built-in Function
The only caveat to using %PASSED is that you won’t know the difference between the parameter being left off completely vs. the special value *OMIT being passed. If your subprocedure wants to treat a parameter differently when it is omitted vs. when it is left off completely, you can use the other new function %OMITTED.
In the above example, Param2 can be either omitted or left off the call entirely. If the code wants to do something differently in each case, it can use the %OMITTED built-in function as follows:
I suspect %OMITTED will not be used nearly as often as %PASSED, since you don’t often need to differentiate between whether a parameter is omitted or not passed. That said, especially in the case of callers that don’t use the prototype, it may be useful to verify that your procedure is being called correctly and be able to give a meaningful error message if it is not.
I’m very glad that IBM continues to improve the RPG language! I’m looking forward to simplifying my parameter handling with %PASSED and %OMITTED. You can check for the PTFs being released by following changes to the RPG Café at the following link: https://www.ibm.com/support/pages/node/6982093
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.