Newsletters   Subscriptions  Forums  Store   Career  Media Kit  About Us  Contact  Search   Home 
fhg
Volume 5, Number 1 -- January 5, 2005

How to Use the *NOPASS and *OMIT Parameter Options

Hey, Joel:


I notice that you use *nopass extensively in your free xRPG procedures to offer flexibility in your procedure calls, but you don't seem to use *omit. I recently came across an example of a prototype that used both for the same parameter. Can you explain the difference between the two, why you use one but not the other, and why someone would use them together like this?

--Rob

Let me start by explaining the two options and how they differ.

*NOPASS

The *nopass parameter option allows you to not pass the parameter at all. Consider the prototype for the getDateStrUSA procedure from my xRPG Core Library :

d getDateStrUSA   pr            10    varying
d  date                           d   const options( *nopass )
d  zero                           n   const options( *nopass )
d  inc                            n   const options( *nopass )
d  sep                           1    const options( *nopass ) 

You'll notice right away that all the parameters use options(*nopass), meaning that they are completely optional. The first rule of *nopass is that every parameter after an optional one is also optional. This means that I have five different parameter lists for calling this procedure:

 /free
    // Get Todays Date as a USA Formatted Date String
    usaDateString = getDateStrUSA();   

    // Get This Date as a USA Formatted Date String
    usaDateString = getDateStrUSA( dateField );

    // Get This Date as a USA Formatted Date String,
    //  not zero-suppressed
    usaDateString = getDateStrUSA( dateField : *off );

    // Get This Date as a USA Formatted Date String,
    //  not zero-suppressed, with separators
    usaDateString = getDateStrUSA( dateField : *off : *on );

    // Get This Date as a USA Formatted Date String,
    //  not zero-suppressed, with separators, use "." as separator
    usaDateString = getDateStrUSA( dateField : *off : *on : '.' );
 /end-free

You'll notice the functionality changes with each parameter, again adding to the flexibility and usefulness of this procedure. This does require some coding discipline in the procedure. First of all, when you allow optional parameters, you must check inside the procedure to see whether the parameters were sent. This is done with the %parms() built-in function.

You also must be aware that while a parameter in a procedure interface defines a local variable in the procedure, you must not use the parameter if it is not sent. The best way I've seen to handle this is to define variables within the procedure that match the incoming parameters and then use the parameter list to set their values. This also allows you to easily establish behavioral defaults for the procedure that are used in the absence of the sent parameters. Below is the code from my getDateStrUSA procedure that handles the parms coming in:

d getDateStrUSA   pi            10    varying
d  date                           d   const options( *nopass )
d  zero                           n   const options( *nopass )
d  inc                            n   const options( *nopass )
d  sep                           1    const options( *nopass ) 

d  dateIn         s               d
d  zeroSuppress   s               n   inz( *on )
d  incSeps        s               n   inz( *on )
d  sepChar        s              1    inz( '/' )

 /free
   select ;
      when  %parms() = 0 ;
        dateIn = %date();
      when  %parms() = 1 ;
        dateIn = date ;
      when  %parms() = 2 ;
        dateIn = date ;
        zeroSuppress = zero ;
      when  %parms() = 3 ;
        dateIn = date ;
        zeroSuppress = zero ;
        incSeps = inc ;
      when  %parms() = 4 ;
        dateIn = date ;
        zeroSuppress = zero ;
        incSeps = inc ;
        sepChar = sep ;
    endsl ;
 /end-free

Notice how I set the default values in the procedure’s D–specs. These values are then overwritten if the parameters were sent. Now the rest of the code uses the Standalone fields defined in the procedure and not the parameter variables defined in the Procedure Interface.

*OMIT

The *omit parameter option allows you to not send a value for a parameter, but, unlike *nopass, it does require you to send a placeholder. If you are not going to send a value, *null must be sent in the parameter location. Using the same example procedure from above, had I used *omit instead of *nopass, the prototype would look like so:

d getDateStrUSA   pr            10    varying
d  date                           d   const options( *omit )
d  zero                           n   const options( *omit )
d  inc                            n   const options( *omit )
d  sep                           1    const options( *omit ) 

Using *omit would also change the way I can call this procedure:

 /free
    // Get Todays Date as a USA Formatted Date String
    usaDateString = getDateStrUSA( *omit : *omit : *omit : *omit );   

    // Get This Date as a USA Formatted Date String
    usaDateString = getDateStrUSA( dateField : *omit : *omit : *omit );

    // Get This Date as a USA Formatted Date String,
    //  not zero-suppressed
    usaDateString = getDateStrUSA( dateField : *off : *omit : *omit );

    // Get This Date as a USA Formatted Date String,
    //  not zero-suppressed, with separators
    usaDateString = getDateStrUSA( dateField : *off : *on : *omit );

    // Get This Date as a USA Formatted Date String,
    //  not zero-suppressed, with separators, use "." as separator
    usaDateString = getDateStrUSA( dateField : *off : *on : '.' );
 /end-free

I hope you agree that this is less elegant than *nopass. The good news is that, unlike *nopass, which requires every parameter after a nopass parameter to also be *nopass, you can pick and choose which parameters you want to be omittable.

Handling the parameter checking in the procedure code is also different. Instead of checking for whether the parameter has been sent, you have to check whether a value or the *null placeholder was sent, by using the %addr() BIF.

d getDateStrUSA   pi            10    varying
d  date                           d   const options( *omit )
d  zero                           n   const options( *omit )
d  inc                            n   const options( *omit )
d  sep                           1    const options( *omit ) 

d  dateIn         s               d
d  zeroSuppress   s               n   inz( *on )
d  incSeps        s               n   inz( *on )
d  sepChar        s              1    inz( '/' )

 /free
    if %addr( date ) = *NULL ;
      dateIn = %date();
    else ;
      dateIn = date ;
    endif ;

    if %addr( zero ) <> *NULL ;
      zeroSuppress = zero ;
    endif ;

    if %addr( inc ) <> *NULL ;
      incSeps = inc ;
    endif ;

    if %addr( sep ) <> *NULL ;
      sepChar = sep ;
    endif ;
 /end-free

Again, I can rely on the default values in the procedure and only change them if the parameter was not omitted.

Using Both Together

One interesting approach is to combine *nopass and *omit:

d getDateStrUSA   pr            10    varying
d  date                           d   const options( *nopass : *omit )
d  zero                           n   const options( *nopass : *omit )
d  inc                            n   const options( *nopass : *omit )
d  sep                           1    const options( *nopass : *omit ) 

I now have an interesting assortment of calling parameter lists. I won't list them all here, but I will answer the question I'm sure you are asking right now: why would anyone do this?

Take the procedure I've been using as an example. The parameters have a series of defaults for the optional parameters. If the programmer wants to use option 4 to change the separator character to something other than the default, but wants the default behavior for parameters 1, 2, and 3, then he must know what values to pass to represent those defaults. Using this approach still allows the caller to have flexibility, without requiring the programmer to know what values to send to represent the default behavior. In this case, it could look like this instead:

 /free
    // Get This Date as a USA Formatted Date String,
    //  zero-suppressed, with separators, use "." as separator
    usaDateString = getDateStrUSA( *omit : *omit : *omit : '.' );
 /end-free

Now the programmer doesn't have to research to remember that he should send %date(), *on, *on for the first three parameters. This could also allow the developer to change the default procedure behaviors without the programmer needing to change the calling code. I'll leave it to the reader to determine the pros and cons of that approach.

Using both options also increases the work you must do in order to handle those incoming parameters. If sent, the *omit placeholder counts as a parameter, just one whose value is null. So basically you have to check for whether a parm was sent and whether it has a value of null:

d getDateStrUSA   pi            10    varying
d  date                           d   const options( *nopass : *omit )
d  zero                           n   const options( *nopass : *omit )
d  inc                            n   const options( *nopass : *omit )
d  sep                           1    const options( *nopass : *omit ) 

d  dateIn         s               d
d  zeroSuppress   s               n   inz( *on )
d  incSeps        s               n   inz( *on )
d  sepChar        s              1    inz( '/' )

 /free
    if %parms() = 0 ;
      dateIn = %date();
    endif ;

    if %parms() > 0 ;
      if %addr( date ) = *NULL ;
        dateIn = %date();
      else ;
        dateIn = date ;
      endif ;
    endif ;

    if %parms()> 1 ;
      if %addr( zero ) <> *NULL ;
        zeroSuppress = zero ;
      endif ;

    if %parms()> 2 ;
      if %addr( inc ) <> *NULL ;
        incSeps = inc ;
      endif ;
    endif ;

    if %parms()> 3 ;
      if %addr( sep ) <> *NULL ;
        sepChar = sep ;
      endif ;
    endif ;
 /end-free

It is true that I favor *nopass over *omit, almost to the point of exclusivity. Originally this was a conscious choice, because the logic of *omit didn't make a lot of sense to me. I mean that if I have to send a parameter, why not send the correct value as well? However, as I develop more and more procedures for other programmers, I find that practical flexibility is of extreme value. For the cost of a little extra parameter checking, I can use both options together to add even more flexibility to a procedure. To me, that is something very worthwhile.

--Joel Cochran



Joel Cochran is the director of research and development for a small software firm in Staunton, Virginia, and is the author and publisher of www.RPGNext.com. Click here to contact Joel by e-mail.


This article has been corrected since its original publication. In the last section of code, the "date" and "dateIn" variable names were presented in the wrong order. IT Jungle regrets the error. [Correction made 1/6/05]

Sponsored By
ITERA

What would it cost your company if your
iSeries was down for 2˝ days?


One large iSeries shop recently experienced a crippling system failure followed by a series of complications that kept the system down for 2˝ days!

Fortunately they had just installed iTera's Echo2 High Availability.

The result: no data was lost, little downtime occurred and users barely had a clue.

Click here to read the full story.


Technical Editors: Howard Arner, Joe Hertvik, Ted Holt,
Shannon O'Donnell, Kevin Vandever
Managing Editor: Shannon Pastore
Contributing Technical Editors: Joel Cochran, Wayne O. Evans, Raymond Everhart,
Bruce Guetzkow, Marc Logemann, David Morris
Publisher and Advertising Director: Jenny Thomas
Advertising Sales Representative: Kim Reed
Contact the Editors: To contact anyone on the IT Jungle Team
Go to our contacts page and send us a message.


THIS ISSUE
SPONSORED BY:

WorksRight Software
iTera
Guild Companies


BACK ISSUES

TABLE OF
CONTENTS
Get Organized with the WDSc Tasks View

How to Use the *NOPASS and *OMIT Parameter Options

Admin Alert: Preparing for an OS/400 V5R1 to V5R3 Upgrade


The Four Hundred
Bingaman Says iSeries Marketing to Focus on Business

Microsoft Extends Laurel Branch to IBM Midrange Shops

TomorrowNow Ramps Up New J.D. Edwards Support Practice

Four Hundred Stuff
Oracle Pledges Support for World ERP System

CCSS Adds Killer Queries, FTP Use to Watch List

Menten Goes for the i-effect

Four Hundred Monitor


Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved.
Guild Companies, Inc. (formerly Midrange Server), 50 Park Terrace East, Suite 8F, New York, NY 10034
Privacy Statement