Additional Free-Format Functions Now

Article ID: 58751

New Functions for Free Format

While writing my new book, Breaking Free, I realized that one of the functions available in the RPGLIB service program (RPGLIB.com) is needed extensively in RPG free format. The MIN function returns the lowest value of a list of values passed to it. So I thought exposing how we did the MIN function, along with its sister function, MAX, would be a good subject for this week's article.

First, if you don't have a copy of RPGLIB, you should consider downloading the 30-day trial at RPGLIB.com; that way you can review the wide range of pre-written subprocedures included in this service program.

As I mentioned, while writing Breaking Free (due in the Spring of 2010), I found an ongoing need to pass in the lowest value of a list of values to various subprocedures. Primarily, this was an issue when calling C runtime functions such as memcpy and CPYBYTES, but there are others.

In traditional RPG syntax, if I have just two values and I need the lowest of the two, I might write something like the following:

    if ( a < b);
        tiny = a;
     else;
        tiny = b;
     endif;

If I have three values, then I might write something like this:

      tiny = a;
    if ( b < tiny);
        tiny = b;
     endif;
     if ( c < tiny);
        tiny = c;
     endif;

If I have four values, then I would continue the above sequence until all the values were checked.

But consider what happens when I use a C runtime function such as memcpy (memory-to-memory copy). This function requires that I pass to it the number of bytes to be copied.

In order to mimic the functionality of the MOVEL or EVAL opcodes, I need to pass the shorter of the two variables that I'm working with. For example, if I want to copy S to T, I would code this:

      memcpy( %addr( t ) : %addr( s ) : %size( t ) );

But is this correct?

What if S is shorter than T? Wouldn't the memcpy function continue copying %size(t) bytes even if it went beyond the end of the S variable? Yes, it would.

What we really want to write is something similar to the following:

      memcpy( %addr( t ) : %addr( s ) : %min( %size( t ) : %size( s ) ) );

Now, the shorter length of the two variable's lengths is used as the number of bytes to copy. This would provide a safe copy.

Of course, the bad news is that no such %MIN built-in function exists. Hopefully we'll see features like %MIN and %MAX in RPG V, but for RPG IV we have to resort to writing this ourselves.

As I mentioned, the RPGLIB service program contains over 200 pre-written add-on features (subprocedures) that can be used in everyday RPG IV programs. Two of those are the MIN and MAX subprocedures.

The MIN subprocedure accepts up to 15 values and returns the smallest of those values. The MAX subprocedure accepts up to 15 values and returns the largest of those values. To provide a basic version of each of these, I'm reproducing their code here, but since most situations call for comparing just two values, I think limiting them to three input values is good enough.

MIN Syntax

min-value = min( value1 : value2 [ : value3 ] )

The first two parameters are required. The returned value is, obviously, the smallest of the two or three input values. So now if we call memcpy, we can write it like this:

      memcpy( %addr( t ) : %addr( s ) : min( %size( t ) : %size( s ) ) );

The only difference here is that instead of an IBM-supplied %MIN built-in function, we're using the MIN subprocedure.

MAX Syntax

max-value = max( value1 : value2 [ : value3 ] )

The first two parameters are required. The returned value is, obviously, the largest of the two or three input values. I wasn't sure how I would use MAX, but it went hand-in-hand with MIN, so I wrote it. Then a customer who uses RPGLIB wrote to say that they use it with the yearly sales analysis/reports. They do something like this:

     maxSales = max(jan : feb : march : april : may : june : july : aug : sept : oct : nov : dec );

Previously, they were placing these fields, which were field names directly from a database record, into an array and then doing a SORTA descending and taking the first element. Now, they can also do quarterly sales analysis, for example:

     QTR1 = max(jan : feb : march );
     QTR2 = max(april : may : june );
     QTR3 = max(july : aug : sept );
     QTR4 = max(oct : nov : dec );

Remember, the RPGLIB versions allow for up to 15 values, but the versions of these functions I'm reproducing here support three values.

Here is the source code for the MIN and MAX subprocedures, preceded by their Prototype source:

MIN Subprocedure Implementation

      D MIN             PR            30P 9
     D  val1                         30P 9 Const
     D  val2                         30P 9 Const
     D  val3                         30P 9 Const OPTIONS(*NOPASS)
    P MIN             B                   export
     D MIN             PI            30P 9
     D  val1                         30P 9 Const
     D  val2                         30P 9 Const
     D  val3                         30P 9 Const OPTIONS(*NOPASS)
     D minValue        S             30P 9
      /free
              minValue = val1;
              if (val2 < minValue);
                  minValue = val2;
              endif;
              if (%parms() >= 3);
                if (val3 < minValue);
                    minValue = val3;
                 endif;
              endif;
           return minValue;
       /end-free
     P MIN             E

MAX Subprocedure Implementation

      D MAX             PR            30P 9
     D  val1                         30P 9 Const
     D  val2                         30P 9 Const
     D  val3                         30P 9 Const OPTIONS(*NOPASS)
    P MAX             B                   export
     D MAX             PI            30P 9
     D  val1                         30P 9 Const
     D  val2                         30P 9 Const
     D  val3                         30P 9 Const OPTIONS(*NOPASS)
     D maxValue        S             30P 9
      /free
              maxValue = val1;
              if (val2 > maxValue);
                  maxValue = val2;
              endif;
              if (%parms() >= 3);
                if (val3 > maxValue);
                    maxValue = val3;
                 endif;
              endif;
           return maxValue;
       /end-free
     P MAX             E

Note that I'm using Packed(30,9) parameter definitions. This choice was made for three reasons:

  1. I wanted to be able to pass numeric values that included decimal digits.
  2. I know that most functions that require integers cast the input values to CONST or VALUE, so my Packed(30,9) would work anyway.
  3. When all else fails, I can wrap MIN and MAX in a %INT built-in function and force an integer.

In most cases, it doesn't matter if I pass integers or values with decimal positions to MIN and MAX, nor does it matter if I need an integer return value or one that includes the decimal digits. This configuration seems to work best.


Bob Cozzi has a new book coming out in the Spring of 2010: Breaking Free; Bob Cozzi's Guide to The Modern RPG Language.

Follow Bob Cozzi on Twitter at: Twitter.com/bobcozzi

Bob Cozzi is the author of RPGLIB the RPG IV add-on or extension library that adds nearly 200 prewritten functions to RPG IV--those that programmers want to use today. Download a free 30-day trial today at RPGLIB.com

RPGWorld.com is Bob's website where you can find all things Cozzi. Including: RPG Open, iWeekly, the most popular RPG IV discussion forum, RPG World Conference, Subprocedure training videos on DVD, and more! Visit RPGWorld.com for details.

ProVIP Sponsors

ProVIP Sponsors