Guru: Assertions, Take 2
February 12, 2024 Ted Holt
It’s been almost 20 years since Cletus the Codeslinger introduced assertions to the IBM midrange world, and in that time, I have included many assertions in my RPG programs. During that 20 years, RPG has changed a bit and Cletus has quit writing articles (but not source code), so I’ve taken it upon myself to update the code he gave us.
First, however, it might be a good idea to briefly review the topic of assertions for readers who don’t know what they are. An assertion is a program statement or command that cancels a program if a fatal condition is found. It provides a way for a programmer to tell the computer that a certain condition ABSOLUTELY, POSITIVELY MUST BE TRUE, OR ELSE!
Here’s an example:
assert (Price = *zero: 'Assertion failed: price is not zero');
This says that if the PRICE variable does not have a zero value at this point, something has gone terribly wrong and disastrous consequences will certainly follow. Therefore the program must be canceled. Assertions always cancel the program, so programmers don’t use them for data validation.
This story contains code, which you can download here.
That’s it in a nutshell. I encourage you to read Cletus’s article for more information before continuing.
RPG has changed in many ways since Cletus shared his routine with us. First, RPG went free-form. I’ve gotten so used to free-form RPG that working on fixed-format RPG source code is about as comfortable as wearing my gum boots on the wrong feet.
Second, IBM added the SND-MSG operation code, which can send escape messages. Underneath the covers, everything’s the same. SND-MSG calls the QMHSNDPM API to send message CPF9898.
Here’s the updated code, both the prototype and the subprocedure itself.
dcl-pr Assert; Condition ind value; Message varchar(80) value options(*nopass: *trim); end-pr Assert; dcl-proc Assert; dcl-pi *n; Condition ind value; Message varchar(80) value options(*nopass: *trim); end-pi; if not Condition; snd-msg *escape Message %target(*caller); endif; return; end-proc Assert;
Notice the message destination: %TARGET(*CALLER). So far this has worked properly for me, no matter how far down in the call stack the SND-MSG is. I have also sent the message to the program boundary by referencing the first subfield of the Program Status Data Structure, and I can’t find any difference in behavior between the two methods.
dcl-ds psds psds; PgmName char(10); end-ds; if not Condition; snd-msg *escape Message %target(PgmName); endif;
The new assert routine is so short that you can copy and paste it from this page, but I put it in the downloadable code in case you prefer it that way.
In fact, the new assert routine is so short that I wonder if we really need an assert subprocedure anymore. Contrast the following statements:
(A) assert (Price = *zero: 'Assertion failed: price is not zero'); (B) if (Price <> *zero); snd-msg *escape 'Assertion failed, price is not zero' %target(*caller); endif;
Does (A) have any advantage over (B)? The only one I can think of it that the word assert sticks out, making the programmer’s intent obvious.
But if the message includes the word assertion, as this example does, why bother with a subprocedure call?
And getting rid of the subprocedure call has to be better for performance.
As I see it, the important thing is not how I write assertions, but that I do write assertions. I like the peace of mind I get from knowing that the data have proper values, and I like my programs to tell me where they canceled and why. It beats spending hours with a debugger trying to recreate a catastrophe.
Thanks Ted! Your tips are always helpful!
He is, in fact, the best.
Someone once said “Don’t look for performance gains until you have performance problems.” If I were writing a real-time or extreme volume program, I might opt for skipping the procedure.
However, 99.99% of the time this won’t be true and for the sake of hiding a dozen-or-so line procedure at the bottom of my program, I’d opt for that approach every time for two reasons.
First, it’s one line versus three. Succinctness greatly improves the readability of code. It takes some skill to write code that works, but much more skill to write code people can understand (including our future selves).
Second… I will never be comfortable with having a semicolon on the if statement line. I suspect RPG is the only language on the planet to do this and it always looks wrong to me. Any opportunity to remove an if is an opportunity to save my sanity.
In the same vein as the simple assert procedure, I also use a simple “cmd( )” procedure that takes away the QCMDEXC name as well has the need to specify length. Now when I need to run a CL command it’s just “cmd(‘DLTF QTEMP/WKFILE’);” and the like. Again… succinctness, and therefore readability, wins.