Midware Ltd.



Sign-up for e-mail notifications

Take our weekly poll

Dow Jones Intraday

Nasdaq Intraday



As we can see from our IsWorkday example, an internal procedure is very similar to a subroutine.  One of the advantages it has over a subroutine is the use of local variables - which may be coded within a procedure without the fear of conflicting with fields in the mainline code.

Another advantage which we haven't talked about yet is the automatic type-casting of parameters.

When a subroutine is called, it relies on certain fields being set correctly prior to it's execution.  For example, if we were to code the IsWorkday procedure as a subroutine, we would create a common field name for the date to be tested.  Each time we executed the subroutine, we would first need to make sure to move the date we want to test to this field.

If we created IsWorkday as a separate program rather than a subroutine or procedure, we would need to call the program by passing parameters.  If you are familiar with how parameters are passed between programs, you know that the value of the parameter is not actually passed.  Rather an address pointer is passed that points to the memory location of the field in the calling program.  When the called program refers to or changes the parameter, it is actually referring to or changing the field from the program where it was called.  This type of parameter passing is called pass by reference.

ILE procedures pass parameters by reference as well, but with one key exception.  If the parameters are the same general type as expected (alpha, numeric, date, etc.) but differ in their attributes (length, packed vs. zoned, *iso vs. *usa), the attributes will automatically be converted (casted).

For example, lets assume we have a procedure that accepts a 7-digit packed numeric field as a parameter.  If we call the procedure with a 5-digit zoned field, a conversion will automatically occur to make the procedure call work correctly.  If you had this attributed mis-match when calling a separate program rather than a procedure, you would get a decimal-data error.

Several important points about procedure parameter typecasting:

  1. Automatic conversion will only occur if the expected parameter and the passes parameter are the same general type.  For example, numeric data will not be converted to character.
  2. Shorter than expected numeric fields can safely be passed to a procedure.  Longer than expected numeric fields can also passed, however if a significant digit is truncated, you will get a run-time overflow error.
  3. Character strings of a greater length than expected may be passed.  Right-side truncation will occur (similar to MOVEL).
  4. The keyword OPTIONS(*VARSIZE) may be coded on the D-specs of the procedure interface and prototype to allow shorter length than expected character strings.  The OPDESC (operational descriptors) keyword should also be used and the CEEDOD API should be called to retrieve the length of the passed field.  We'll an example of this later.

Note that in the IsWorkday procedure we coded the date field parameter with DATFMT(*iso).  We can safely call the procedure with a date of a different format and the conversion will occur automatically.

If, however, we would have specified a DATFMT(*ymd) (or any data format with a two-digit year), and tried to call the procedure with a 4-digit year date format outside the valid range for a 2-digit year (January 1, 1940 to December 31, 2039) we would get a run-time exception.

This brings up a good point.  Procedures should always be coded to be as flexible as reasonably possible, requiring as little work as possible from the end-user programmer, and as bullet-proof as possible.  As we begin to use more advanced procedure techniques, it will be more challenging to debug problems than if they were in the mainline code.

Looking at our IsWorkday example, we may find that passing a parameter of a date-type field may not be convenient.  Most of the dates we test may be in a 6 or 8-digit numeric field.  To use the procedure as it is now, we will always need to convert the numeric field to a date-type field, and then call the procedure.  Also, recall that before we move a number to a date-field, we should test it first for a valid date.

This means that everytime before we use the procedure (assuming our dates are in a numeric format) we need to (1) setup a temporary date field in D-specs, (2) test the numeric date for validity, (3) evaluate the result of the test, and (4) move the number to a date field.  All this preliminary work contradicts our statement about making procedure as easy to use as possible.

Therefore, lets take advantage of automatic typecasting and change our procedure to be more user friendly.

Changing IsWorkday to accept a numeric parameter

We are now going to change IsWorkday so that instead of forcing the calling code to convert numeric dates to a date-type field, it will accept a numeric field and do the conversion for you.

Lets look at the new procedure interface to allow this:

D IsWorkday       PI              N
D  @Date                         8  0

Don't forget to also change the prototype similarly

Note that we are accepting an 8-digit date.  Because of typecasting, it will also automatically accepts 6-digit dates also.  We also need to setup a local variable in the procedure for the converted date:

D @TestDate       S               D

We will now test the numeric parameter for a valid date format.  We are going to assume that the date is passed either in CCYYMMDD or YYMMDD formats.  If your application system has mixed date formats in it, you can either write another procedure to convert dates between formats, or accepts a second parameter of the date format being passed.

 * If the user passed a date less than 100/00/00 we are
 * going to assume a six-digit date was passed, rather
 * an eight-digit date with a century code of "00"...
C                   if        @Date < 1000000
C     *ymd          test(d e)               @Date
C                   else
C     *iso          test(d e)               @Date
C                   endif

If an invalid date was passed, the built-in-function %ERROR will return a value of true, and we want to return a value a boolean value of false (*off) because the date is not a workday.  Otherwise, we want to move the numeric date to a date-type data field, and test it as we did before.

C                   if        %error
C                   eval      @WorkDay = *off
C                   else
C                   if        @Date < 1000000
C     *ymd          move                    @TestDate
C                   else
C     *iso          move                    @TestDate
C                   endif
 * How many days are there between our base date and
 * the passed date?
C     @BaseDate     subdur    @Date         @Days:*d
 * Based on the number of days, figure out the day of
 * week of the passed date (0=Sunday, 1=Monday, etc.)
C                   eval      @DayOfWeek = %rem(@Days: 7)
 * If date falls on a weekend, it is not a workday...
C                   if        @DayOfWeek = 0 or
C                             @DayOfWeek = 6
C                   eval      @WorkDay = *off
 * Otherwise, it is...
C                   else
C                   eval      @WorkDay = *on
C                   endif
C                   endif
C                   return    @WorkDay

With the procedure written like this, we can now test either 6 or 8-digit dates without having to first convert them to a date-type field:

C                   if        IsWorkday(@Date6)

C                   eval      *in99 = IsWorkday(@Date8)

  Back to Calling a Procedure

Next to External Procedures

Home Feedback Contents Search

Send mail to midware@midwareservices.com with questions or comments about this web site.
Copyright 2000 Midware, Ltd.

Last Modified:  August 30, 2000