Often, when I use an API or C runtime function in RPG IV, I need to pass the minimum of two or more values to the API or C function. So I end up doing this:
if (%size(a) < %size(b));
minLen = b;
else;
minLen = a;
endif;
callp myFoo(var1:var2: minLen);
/end-free
Most other languages have a macro or built-in function that can easily return the minimum or maximum lengths of two or more values. For RPG IV, I've long abandoned this practice of conditionally calculating the minimum value and have been using my own RPG xTools' MIN and MAX subprocedures, like so:
Certainly, this is subjectively easier, although I still prefer the following:
Until IBM provides us with a built-in function, I've decided to post the simple code behind my MIN and MAX subprocedures. They aren't rocket science, so why not?
The MIN and MAX subprocedures return the numeric minimum or maximum value, respectively. The input values are converted to Packed(30,9), which is compatible with V5R1 and early releases of OS/400. The original code is in fixed-format RPG IV, to make it pre-V5R1 compatible, but I know how much this audience prefers free-format, so I've converted it for you.
D Min PI 30P 9
D nValue1 30P 9 Const
D nValue2 30P 9 Const
D nValue3 30P 9 Const OPTIONS(*NOPASS)
D nValue4 30P 9 Const OPTIONS(*NOPASS)
D nValue5 30P 9 Const OPTIONS(*NOPASS)
D nValue6 30P 9 Const OPTIONS(*NOPASS)
D nValue7 30P 9 Const OPTIONS(*NOPASS)
D nValue8 30P 9 Const OPTIONS(*NOPASS)
D nMin S 30P 9
/free
if (%parms() < 1);
nMin = 0;
endif;
if (%parms() >= 1);
nMin = nValue1;
endif;
if (%parms()>= 2 and
nMin > nValue2);
nMin = nValue2
endif;
if (%parms()>= 3 and
nMin > nValue3);
nMin = nValue3;
endif;
if (%parms()>= 4 and
nMin > nValue4);
nMin = nValue4;
endif;
if (%parms()>= 5 and
nMin > nValue5);
nMin = nValue5;
endif;
if (%parms()>= 6 and
nMin > nValue6);
nMin = nValue6;
endif;
if (%parms()>= 7 and
nMin > nValue7);
nMin = nValue7;
endif;
if (%parms()>= 8 and
nMin > nValue8);
nMin = nValue9;
endif;
return nMin;
/end-free
P Min E
The MAX subprocedure is effectively the same, so I'm not going to reproduce it here. You can easily clone the MIN function and change the names. One exception is that the conditional statements in MAX would be as follows:
if (%parms() >= 2 and
nMax < nValue2);
nMax = nValue2;
endif;
/end-free
Where to Apply Min and Max
Using these two functions is more pervasive in my code than I first thought. Since I'm a big fan of using C runtime functions in RPG IV, I use the MIN subprocedure extensively. The quintessential example is the C runtime memcpy() function. Unlike in RPG IV, in C you have to specify the length of the field being copied. You also have to worry about not overwriting the target. For example, in RPG IV this works famously:
D First S 10A
D nPos S 10I 0
/free
nPos %scan(' ' : name);
if (nPos > 0);
first = %subst(name:1:nPos-1);
endif;
/end-free
At the end of the routine, the name "Robert" is copied to the field named FIRST. Using the C runtime memcpy() function, you might code this:
D First S 10A
D nPos S 10I 0
/free
nPos %scan(' ' : name);
if (nPos > 1);
memcpy(%addr(first) : %addr(name) : nPos-1);
endif;
/end-free
On the surface, this might look great, but what happens when the first name is more than 10 positions long? Using the RPG IV method, it would just work, as RPG knows about field lengths. But the C runtime would cause a "learning experience." So we use our trusty MIN function to solve this problem, as follows:
D First S 10A
D nPos S 10I 0
/free
nPos %scan(' ' : name);
if (nPos > 1);
memcpy(%addr(first) : %addr(name) :
min(%size(first):nPos-1));
endif;
/end-free
By wrapping the length of the field named FIRST and the number of bytes to copy (i.e., nPos-1) in MIN(x:y), the lowest of the two values is returned, so you never have an integrity issue.
Of course, you now need to make sure you clear the FIRST field before calling memcpy(), as follows:
nPos %scan(' ' : name);
if (nPos > 1);
first = ' '; // Clear the target.
memcpy(%addr(first) : %addr(name) :
min(%size(first):nPos-1));
endif;
/end-free
A great use for memcpy() is copying an array to a field or a field to an array. This way, no data structures or tricks are needed. For example:
D Libs S 11A Dim(255)
D LibNames S 2805A
/free
nBytes = min(%size(LibNames):%size(libs:*ALL));
memcpy(%addr(libs) : %addr(libnames) : nBytes);
/end-free
In the example above, the nBytes field is initialized to the lesser length of the LIBNAMES field and the total length of the LIBS array (all elements). As illustrated, both are the same length, so nothing would happen regardless of which size you used for the memcpy() function. But what if LIBNAMES was shorter? For example:
D Libs S 11A Dim(255)
D LibNames S 275A
/free
nBytes = min(%len(LibNames):%size(libs:*ALL));
memcpy(%addr(libs) : %addr(libnames) : nBytes);
/end-free
Suddenly, it becomes important to use the lesser of the two lengths.
Accurate programming is extremely important when using non-RPG IV interfaces, such as C and MI. While it isn't as critical as Windows apps, which may cause the computer to reboot, using poor programming practices could cause your job/session to go crazy and even end abnormally. Do you really want that?
Bob Cozzi is author of the best-selling The Modern RPG IV Language, Fourth Edition as well as RPG TNT: 101 Dynamite Tips 'n Techniques with RPG IV and is host of the i5 Podcast Network, which provides free video and audio podcasts to the i5 community. You can also see him in person at RPG World in May 2007.
LATEST COMMENTS
MC Press Online