Back in April, in the iSeries Network's iSpeak blog, I wrote an entry about the proposed support for pointers in the CL language. Because many CL programmers read this newsletter, I thought it might be of interest in Club Tech iSeries Programming Tips, as well.
At the spring COMMON conference, I had the privilege of meeting Guy Vig or, as he was introduced to me, "Mr. CL." He and I had a conversation about the future of the CL programming language.
Out of the blue, IBM made some profound changes to the CL language in V5R3. In fact, not since ILE support was introduced in V3R1 has CL had such significant changes made to it.
The next release of OS/400 (the one that follows V5R3) promises to continue to offer CL improvements. The proposed changes include support for pointers and defined-on variables that work like data structures.
You might be saying to yourself, "Why would I want to use pointers in CL?" or even, "Pointers?! That's a bad idea! Leave the complicated stuff for C programmers!" At first, I agreed with those sentiments. However, as I thought about it a little more, I warmed up to the idea.
To explain why I think CL could use pointer and based-on variable support, let me clarify what I feel the purpose and goal of the CL language is. The name "CL" stands for "Control Language" and also explains the language's purpose. Its purpose is to control the flow of a job, and certain things are important for doing that:
Finally, my point: For the ability to call programs, call them conditionally, and loop between them to be effective, you need to be able to pass data between them. For CL to be able to pass data correctly, it needs support for the various data types available in the HLLs that it calls! HLLs have support for passing pointers as parameters, and therefore CL should be able to receive those parameters!
Just think if CL didn't have support for the *CHAR (character) data type! How would an RPG program set a variable that could be passed to a different program later? Likewise, a CL program can read a *DEC (packed decimal) from one program and pass it to another. It should have the same support for pointers and data structures!
The following code is a sample program intended to show off the power of these enhancements. It does something that you couldn't easily do in previous versions of CL: It reads the contents of a directory in the IFS. For each tab-delimited file that it finds in the directory, it uses CPYFRMSTMF to convert it to a PF later processed by an RPG program:
PGM
DCL VAR(&NUL) TYPE(*CHAR) LEN(1) VALUE(x'00')
DCL VAR(&DIRNAME) TYPE(*CHAR) LEN(200)
DCL VAR(&EXT) TYPE(*CHAR) LEN(4)
DCL VAR(&POS) TYPE(*INT) LEN(4)
DCL VAR(&SUCCESS) TYPE(*INT) LEN(4)
DCL VAR(&HANDLE) TYPE(*PTR)
DCL VAR(&ENTRY) TYPE(*PTR)
DCL VAR(&NULLPTR) TYPE(*PTR)
DCL VAR(&STMF) TYPE(*CHAR) LEN(640)
/**********************************************************/
/* This works like a data structure It's 796 chars */
/* long with a field called &NAMELEN in positions 53-56 */
/* and a field called &NAME in positions 57-640 */
/**********************************************************/
DCL VAR(&DIRENT) TYPE(*CHAR) LEN(796) +
STG(*BASED) BASPTR(&ENTRY)
DCL VAR(&NAMELEN) STG(*DEFINED) DEFVAR(&DIRENT 53) +
TYPE(*UINT) LEN(4)
DCL VAR(&NAME) STG(*DEFINED) DEFVAR(&DIRENT 57) +
TYPE(*CHAR) LEN(640)
/**********************************************/
/* Open the /edi/incoming/custdata directory: */
/* the API returns a NULL pointer to indicate */
/* failure. */
/**********************************************/
CHGVAR VAR(&DIRNAME) VALUE('/edi/incoming/tabdata' *CAT &NUL)
CALLPRC PRC('opendir') PARM((&DIRNAME)) RTNVAL(&HANDLE)
IF (&HANDLE *EQ &NULLPTR) DO
SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('Unable +
to open directory!') MSGTYPE(*ESCAPE)
ENDDO
/**********************************************/
/* Read the contents of the directory, and */
/* convert each stream file that ends with */
/* '.TAB' to records in a physical file: */
/**********************************************/
CALLPRC PRC('readdir') PARM((&HANDLE *BYVAL)) +
RTNVAL(&ENTRY)
DOWHILE COND(&ENTRY *NE &NULLPTR)
CHGVAR VAR(&STMF) VALUE(%SST(&NAME 1 &NAMELEN))
/* extract the last 4 characters from the filename */
IF (&NAMELEN *GE 4) DO
CHGVAR VAR(&POS) VALUE(&NAMELEN - 3)
CHGVAR VAR(&EXT) VALUE(%SST(&STMF &POS 4))
ENDDO
ELSE DO
CHGVAR VAR(&EXT) VALUE(' ')
ENDDO
/* if the last 4 are '.TAB' add to our PF, and +
delete the original so that it doesn't get +
processed again */
IF ((&EXT *EQ '.TAB') *OR (&EXT *EQ '.tab')) DO
CPYFRMIMPF FROMSTMF(&STMF) TOFILE(TRANMAST) +
MBROPT(*ADD) RCDDLM(*CRLF) FLDDLM(*TAB) +
RPLNULLVAL(*FLDDFT)
DEL OBJLNK(&STMF)
ENDDO
CALLPRC PRC('readdir') PARM((&HANDLE *BYVAL)) +
RTNVAL(&ENTRY)
ENDDO
/**********************************************/
/* Close the directory */
/**********************************************/
CALLPRC PRC('closedir') PARM((&HANDLE *BYVAL)) +
RTNVAL(&SUCCESS)
/**********************************************/
/* Call an RPG program to process the new */
/* transactions */
/**********************************************/
CALL PGM(PROCESS)
ENDPGM
The IFS APIs are just one example. Many useful APIs become accessible with pointer support! Other examples include string-handling functions and database reading/writing/updating. The only thing preventing you from doing this in CL in V5R3 is the lack of a pointer data type.
You can read the original blog entry and readers' comments at the following link:
http://www.iseriesnetwork.com/iSNblogs/iSpeak/archives/2005/04/pointers_in_cl.html [2]
Links:
[1] http://systeminetwork.com/author/scott-klement
[2] http://www.iseriesnetwork.com/iSNblogs/iSpeak/archives/2005/04/pointers_in_cl.html