The use of prototypes has lead to the widespread usage of /COPY members in virtually every new RPG IV source member. One thing that people don't often realize, however, is that you can store other things besides prototypes in these /COPYs. One component I often include is named constants. This lets the users of my subprocedures use special values as parameters. For example, if I have a subprocedure with a parameter that can accept any of three different values, I will code those values as named constants.
D First C Const(1)
D Second C Const(2)
D Third C Const(3)
Then when my subprocedure is called, the programmer can write something like the following:
myProc(custno : second);
Note the use of the SECOND named constant. This provides a more documented value than simply hard-coding the number 2. These three named constants would be included in the /COPY member that also included the prototype for the MYPROC subprocedure. That way the programmer using MYPROC can take advantage of these values, which are arguably more memorable than the numbers themselves. Let's look at a concrete example.
To illustrate how this would all fit together, let's look at a hybrid subprocedure named CONVERT. This subprocedure's purpose is to convert the data passed into it to all uppercase or all lowercase letters. The controlling parameter is the third parameter, which accepts a 0 or 1 as its value. Since I can never remember whether 0 means uppercase or 1 means lower case or vice versa, I've created two named constants, UPPER and LOWER, that may be passed to the subprocedure. Here's their declaration:
D UPPER C Const(0)
D lower C Const(1)
The nice thing about using the named constant approach is that you can name the constants anything you want: UPPER, ToUpper, UPIFY, etc.
The following is a source member named CONVERT that includes the prototype for CONVERT and the named constants UPPER and LOWER.
/if NOT DEFINED(CONVERTCASE)
/define CONVERTCASE
D convert PR 10I 0 extProc('CONVERT')
D inString 65535A OPTIONS(*VARSIZE)
D inLen 10I 0 Const
D option 10I 0 Const
D UPPER C Const(0)
D LOWER C Const(1)
/endif
The named constants UPPER and LOWER are stored in the same /COPY source member as the prototype named CONVERT. Anyone who uses the CONVERT /COPY and its CONVERT subprocedure will also have access to those named constants.
The CONVERT source member is a working subprocedure prototype and requires a couple other components: A data structure and an API prototype. The full and complete source member containing the CONVERT prototype and its related components is featured below.
/if NOT DEFINED(CONVERTCASE)
/define CONVERTCASE
D convert PR 10I 0 extProc('CONVERT')
D inString 65535A OPTIONS(*VARSIZE)
D inLen 10I 0 Const
D option 10I 0 Const
D UPPER C Const(0)
D LOWER C Const(1)
/IF NOT DEFINED(FRCB_T)
/define FRCB_T
******************************************************
** Control structure for QlgConvertCase
******************************************************
D FRCB_T DS Align Qualified
D reqType 10I 0 Inz(1)
D CCSID 10I 0 Inz(0)
D case 10I 0 Inz(LOWER)
D reserved 10A Inz(*ALLX'00')
/endif
/IF NOT DEFINED(QLGCONVERTCASE)
/define QLGCONVERTCASE
******************************************************
** Prototype for QlgConvertCase
******************************************************
D QlgCvtCase PR extProc('QlgConvertCase')
D ctrlBlock LikeDS(FRCB_T) Const
D inString 65535A Const Options(*VARSIZE)
D OutString 65535A Options(*VARSIZE)
D nLength 10I 0 Const
D APIErrorDS 16A OPTIONS(*VARSIZE)
/endif
/endif
To show how this can be used, I've created an example source member that implements the CONVERT subprocedure and shows how to call it using the named constants.
H OPTION(*NODEBUGIO:*SRCSTMT)
/if DEFINED(*CRTBNDRPG)
H DFTACTGRP(*NO)
/ENDIF
/INCLUDE RPGCODER/QCPYSRC,convert
D myFRCB DS LikeDS(FRCB_T) Inz(*LIKEDS)
D myApiErrorDS S 16A Inz(*ALLX'00')
D myName S 32A Inz('Bob Cozzi')
C MOVE *ON *INLR
/FREE
convert(myName : %len(%TrimR(myName)) : UPPER);
convert(myName : %len(%TrimR(myName)) : lower);
return;
/END-FREE
P convert B
/if DEFINED(*CRTRPGMOD)
P EXPORT
/endif
D convert PI 10I 0
D inString 65535A OPTIONS(*VARSIZE)
D inLen 10I 0 Const
D option 10I 0 Const
/free
if (inLen = 0);
return 0;
endif;
myApiErrorDS = *ALLX'00';
myFRCB.case = option; // Convert to upper/lower case
QlgCvtCase(myFRCB : inString : inString : inLen : myApiErrorDS);
return inLen;
/end-free
P convert E
Copy this source code to your QRPGLESRC source member. If you've been following along, store it in the CONVERT source member in QRPGLESRC in the RPGCODER library; however, any library will work. Then add the prototype source member named CONVERT to the QCPYSRC source file in RPGCODER. To compile, just use PDM option 15 (CRTBNDRPG).
I like to include the DBGVIEW(*SOURCE) and then run a test to see the results in debug mode.
The QlgConvertCase is a system API that converts between upper/lower case letters using NLS (nation language support) and taking the CCSID into consideration. Other conversion methods, such as using the RPG IV %XLATE built-in function are tied to the CCSID of the program and don't often work when the program is pushed onto a System i that isn't in North America. The QlgConvertCase API is the right way to do upper/lower case conversion.
Bob Cozzi lectures on RPG IV and System i development to corporate customers and user groups all year long. He is author of RPG TnT: 101 Dynamic Tips 'n Techniques with RPG IV and hosts iWeekly, a weekly video podcast for RPG developers and managers aired live on RPGWorld.com at noon Eastern time every Friday (16:00 GMT). Bob also produces RPG World, the most popular RPG IV developer conference of the year. The next RPG World conference is planned for May 2009 in Las Vegas. Start planning now for your 2009 trip to RPG World and join Bob Cozzi, Bruce Hoffman, Greg Veal, Aaron Bartell, and more for the best RPG and System i developer training you can find. Details are posted at RPGWorld.com.