APIs have been a hot topic among enthusiastic AS/400 programmers since Release 3.0 of OS/400. Even though IBM has given us only a taste of Application Programming Interfaces with Release 3.0, these 23 APIs are powerful enough to prompt us into trying them out for size. After scouring the System Programmer's Interface Reference manual for information on the application programming interfaces (APIs), we come face-to-face with a totally new concept of programming for us RPG and COBOL coders -- pointer-based languages.
Most of the Release 3.0 APIs work fine with RPG or COBOL, and even CL, but the inference in the System Programmer Interface manual is to use pointer-based languages. Also, in a recent interview with News 3X/400, Ron Fess, project manager for IBM's Management System Openness on the AS/400, said, "The handwriting is on the wall.... It's a question of how fast we move in that direction..." in response to questions about IBM's move to C as the systems programming language of choice. C, along with Pascal, PL/I and MI, is one of the pointer-based languages available on the AS/400.
Fess also suggested in the News 3X/400 (March 1991) interview that, although many future APIs will be compatible with RPG and COBOL, certain APIs will only be usable through C. This is already true for three of the Release 3.0 APIs -- user queues, user indexes and change pool attributes.
The List APIs all use a new Release 3.0 object type -- user space -- to hold the retrieved information. For more information on user spaces, see the article, "Anatomy of a User Space," in this issue of Midrange Computing. Once the List API stores the retrieved job, spool or object information to the user space, the user space must be processed. This is where the pointer-based languages are highly efficient. RPG and COBOL were designed to work with standard record formats -- not the variable data typically retrieved by the List APIs. C, and the other pointer languages, can directly reference a user space's variable data, whereas RPG and COBOL must use the QUSRTVUS API to access this data.
If the handwriting is on the wall, and if future APIs will be geared to C, and if APIs work more efficiently with C, then those of us who wish to exploit the AS/400 to its fullest potential need to learn about pointer languages. I will be describing C pointers in detail, along with their use with the APIs. We will compare RPG and C coding techniques, that is, a non-pointer language against a pointer language, by looking at fragments of the example programs found in IBM's System Programmer's Interface Reference. You also will be introduced to C's data types and its powerful data structure definitions.
The Point Is
Kernighan and Ritchie, in their book, The C Programming Language, (the de facto standard of C books) state: "A pointer is a variable that contains the address of another variable." Think about this definition while you look over 1. Notice that the pointer variable, prt_2_Char, contains the memory address of the variable Char_Var and thus points to the value A. This seemingly simple concept yields great programming power and -- complexity.
Kernighan and Ritchie, in their book, The C Programming Language, (the de facto standard of C books) state: "A pointer is a variable that contains the address of another variable." Think about this definition while you look over Figure 1. Notice that the pointer variable, prt_2_Char, contains the memory address of the variable Char_Var and thus points to the value A. This seemingly simple concept yields great programming power and -- complexity.
Some of the basic issues of pointer use include: definition, initialization, indirection and pointer arithmetic. In C, pointers are defined explicitly by:
type *variable;
'type' is any valid C data type and 'variable' is the name of the pointer variable. 2 contains a list of valid C data types. To manipulate and use data referred to by pointers we need to use the pointer operators '*' and '&'. The pointer operators are commonly referred to as: "at address" or "actual value of" for the asterisk '*' and "address of" for the ampersand '&'. In regard to our example in 1, these would be the variable definitions:
'type' is any valid C data type and 'variable' is the name of the pointer variable. Figure 2 contains a list of valid C data types. To manipulate and use data referred to by pointers we need to use the pointer operators '*' and '&'. The pointer operators are commonly referred to as: "at address" or "actual value of" for the asterisk '*' and "address of" for the ampersand '&'. In regard to our example in Figure 1, these would be the variable definitions:
char *ptr_2_Char;
char Char_Var;
and then: char_Var = 'A';
ptr_2_Char = &Char_Var;
would have initialized Char_Var to the EBCDIC value for 'A' and set ptr_2_Char to the address of the variable Char_Var. To refer to the value ptr_2_Char points to we need to use the actual value of operator:
*ptr_2_Char = 'B';
The asterisk (*) indicates that we want to access the data at the memory location referred to by ptr_2_Char, not the address itself. Restated another way: move the value 'B' into the memory location to which ptr_2_Char refers. Now both *ptr_2_Char and Char_Var are equal to 'B' since the pointer value in the variable ptr_2_Char is the same as the address of Char_Var. This doesn't mean that ptr_2_char always has to point tp Char_Var, we can easily change it to point elsewhere by setting the address of another character variable to the pointer variable ptr_2_Char:
ptr_2_Char = &Another_Char_Var;
RPG/400 does not allow pointers to be defined, but RPG parameters are a good example of how pointers work. In RPG, when a variable is passed to another program, the address of the data is passed. The calling program then receives the address of the data to an entry parameter for manipulation. When control returns to the calling program, the passed variable retains any modifications made to it by the called program.
C, on the other hand, passes parameters to other C functions by value. Passing parameters by value means that the variable's value in the calling program is copied into temporary storage; it is the temporary storage that is received by the called program. In C, the calling program must pass the address of the variables to the called program in order to have the parameter's modified value returned. The program being called must have its entry parameters defined as pointers.
Pointers can become quite complex. For instance, a pointer variable can be defined to point to a pointer:
char **ptr_2_ptr;
Which is basically a two-level array. In fact, the standard C entry parameter list for a program's Main() function uses a two-level array. In 5, line 8, IBM has described the second parameter of the main function to be an array of pointers to characters. There is a close parallel between C pointers and arrays since an array variable, without an index, is a pointer to the address of the first byte of data in the array. The only significant difference is that with pointers, you use pointer arithmetic to traverse contiguous bytes of data, whereas arrays use indexing. Line 7 of this example, and all Main() functions that need a parameter list, describe an integer variable to hold the count of the actual parameters.
Which is basically a two-level array. In fact, the standard C entry parameter list for a program's Main() function uses a two-level array. In Figure 5, line 8, IBM has described the second parameter of the main function to be an array of pointers to characters. There is a close parallel between C pointers and arrays since an array variable, without an index, is a pointer to the address of the first byte of data in the array. The only significant difference is that with pointers, you use pointer arithmetic to traverse contiguous bytes of data, whereas arrays use indexing. Line 7 of this example, and all Main() functions that need a parameter list, describe an integer variable to hold the count of the actual parameters.
You might ask, "Is pointer arithmetic anything like 'New Math'?" It's pretty simple, really. A pointer variable can be added to and subtracted from, as in:
ptr_2_Char = prt_2_Char + 1;
or ptr_2_Char += 1;
or ptr_2_Char++;
All three of these operations are equivalent in that they increment the address in the pointer variable by the length of the pointer's data type. All pointer arithmetic is relative to its base type. So, in the above examples, the address would be incremented by one whereas a pointer to an integer would increment by four. I must mention here that there is no bounds checking in C. You can loop though pointer arithmetic indefinitely...or until, on an AS/400, your program exceeds its work space and aborts. On a PC, it crashes. Seatbelts not provided with C; proceed with caution!
But pointer arithmetic is why C handles strings so well. C programs can quickly and easily traverse a string, copy strings, or return the address of the subset of a string. These and many other pointer-based techniques are why so many word processors were written in C.
Pointers can also point to data structures, an explanation of which brings us back to the APIs. The user spaces, created to hold the information (about jobs, spool file members, data file field record descriptions, etc.) all have a particular data structure associated with them. These structures are fairly lengthy, but they are all defined for us in the QUSRTOOL library -- for RPG and COBOL, as well as for C. 3 contains three of QUSRTOOL's C and the corresponding RPG data structures that the System Programmer's Interface Reference uses in its delete spool file example programs -- SPOOLINFO.
Pointers can also point to data structures, an explanation of which brings us back to the APIs. The user spaces, created to hold the information (about jobs, spool file members, data file field record descriptions, etc.) all have a particular data structure associated with them. These structures are fairly lengthy, but they are all defined for us in the QUSRTOOL library -- for RPG and COBOL, as well as for C. Figure 3 contains three of QUSRTOOL's C and the corresponding RPG data structures that the System Programmer's Interface Reference uses in its delete spool file example programs -- SPOOLINFO.
The first token in 3, type def, is an example of how C allows a programmer to create unique type definitions. This definition can then be used to declare other variables. The type definition header_struct (3) is used in IBM's example program in the System Programmer's Interface Reference. The SPOOLINFO code fragments in 5 uses:
The first token in Figure 3, type def, is an example of how C allows a programmer to create unique type definitions. This definition can then be used to declare other variables. The type definition header_struct (Figure 3) is used in IBM's example program in the System Programmer's Interface Reference. The SPOOLINFO code fragments in Figure 5 uses:
header_struct *space;
to define a pointer to a memory location that contains data formatted like the structure definition header_struct.
To refer to an individual variable within the data structure, header_struct, that space points to; we need to introduce two new pointer operators: the . (dot) and the -> (arrow). The dot operator is used when working with the actual structure. The arrow operator is used when a pointer to a structure is used. The operators reference individual elements of structures and in C, you must fully qualify variables (unlike COBOL, where qualification is an option). RPG definitely does not let you qualify a DS structure variable. This inability to qualify a variable is why an RPG program can't use the same variable name in different structures or different files. Incidentally, IBM's C/400 and ASNA's Diploma/C both use data-structures for record formats. In the SPOOLINFO C program, to reference the header structures, number-of-entries element, pointed to by the variable, space, IBM uses:
space->number_of_entries.
Had the variable, space, not been defined as a pointer but as variable to hold the actual data as in:
header_struct space;
then we would have referenced number-of-entries by:
space.number_of_entries.
But that would have completely changed the logic of the whole program by doing away with the use of pointers and pointer arithmetic. The resulting C code would have been no better than IBM's other delete spool file API RPG example. IBM's C SPOOLINFO example uses a combination of pointer arithmetic and data structures to more efficiently traverse the user space -- resulting in a faster program. In the RPG example, the API call, QUSRTVUS, must be used multiple times to access each spool file entry in the user space. 4 uses action diagrams to describe the program logic for both versions of the SPOOLINFO program.
But that would have completely changed the logic of the whole program by doing away with the use of pointers and pointer arithmetic. The resulting C code would have been no better than IBM's other delete spool file API RPG example. IBM's C SPOOLINFO example uses a combination of pointer arithmetic and data structures to more efficiently traverse the user space -- resulting in a faster program. In the RPG example, the API call, QUSRTVUS, must be used multiple times to access each spool file entry in the user space. Figure 4 uses action diagrams to describe the program logic for both versions of the SPOOLINFO program.
In both examples shown in 5, a user space is first created to hold spool file data retrieved from the single call to the QUSLSPL API. Both the RPG and C programs also fill the user space with QUSLSPL and retrieve spool file attributes with QUSSPLA. The difference lies in how the two programs access data from the user space. In RPG, the QUSRTVUS call is used with a hard-coded offset to pull the list starting position, list size, and the number of entries variables from the header portion of the user space (lines 7-13). The list starting position and size are then used for subsequent calls to QUSRTVUS (lines 14-19) to process each entry. The list starting position is incremented after each call to QUSRTVUS by the list size, making it equal to the offset of the next entry (line 21). The number of entries variable is used to control the loop (line 14).
In both examples shown in Figure 5, a user space is first created to hold spool file data retrieved from the single call to the QUSLSPL API. Both the RPG and C programs also fill the user space with QUSLSPL and retrieve spool file attributes with QUSSPLA. The difference lies in how the two programs access data from the user space. In RPG, the QUSRTVUS call is used with a hard-coded offset to pull the list starting position, list size, and the number of entries variables from the header portion of the user space (lines 7-13). The list starting position and size are then used for subsequent calls to QUSRTVUS (lines 14-19) to process each entry. The list starting position is incremented after each call to QUSRTVUS by the list size, making it equal to the offset of the next entry (line 21). The number of entries variable is used to control the loop (line 14).
The C example directly references user space information through the pointer returned from the QUSPTRUS API (line 10). With the pointer variable, space, now holding the address of the first byte of the user space, we need to simply add the list section offset found in the header_struct data-structure to point to the first spool file entry (line 13). The C program initializes the pointer variable, list_section, to hold the address of the first byte of the list portion of the user space by setting it equal to the pointer, space, plus the list section offset (lines 12-13). The pointer to the list section is then moved into the pointer variable, entry_list. This pointer variable is used to hold the address of the current entry being processed. There are then three pointer variables used:
o space -- address of the first byte of the user space
o list_section -- address of the first byte of the list section within the user space
o entry_list -- address of the current list entry being processed within the user space
Using pointer arithmetic, it is simple to jump from one section of the user space to the next. The double pluses (++) after entry_list increment the address by the length of the data structure -- splf0100. The variable, entry_list, then, effectively points to the next list entry.
On lines 12 and 14, the (char *) and the (splf0100 *) may seem somewhat obscure to you. In C, this is know as casting, and is used to ensure that the value being assigned is of the same type, or cast, of the receiving variable. This is important, for if we had not cast list_section as the data structure type splf0100, the pointer arithmetic in line 22, would not have incremented entry_list relative to the appropriate base type. There is no telling where entry_list would have pointed to then.
If you still don't see the "point," consider this: user spaces can be directly manipulated by pointer-based languages. "So what? You've already covered that anyway," you say. Well, this capability may seem trivial to you, but it allows C programmers to code on the AS/400 using some highly sophisticated techniques previously not directly supported.
I've already mentioned word processors, but consider cross reference tools, sorts, spreadsheets and even compilers. C on the PC has been used to develop many sophisticated and complicated products including: Lotus 1-2-3, Microsoft Word and Microsoft Excel. With C on the AS/400 coming closer and closer to its PC counterpart, hopefully, many of these products will be ported to the AS/400. C isn't for everyone by a long shot, but it's nice to know it's there if you need it.
C and APIs -> What's the Point?
Figure 1 Pointers
Figure 1: Pointers Memory Variable Variable Address in Memory Name 1000 1003 || *ptr_2_Cha 1001 | 1002 | 1003 A <--/ Char_Var 1004 X 1005 E Another_Char_Var
C and APIs -> What's the Point?
Figure 2 Valid C types
Figure 2: Valid C Types TYPE DESCRIPTION RPG EQUIVALENT char - unsigned byte holding EBCDIC chaaracter CHAR int - signed value 4 bytes long BIN(4) float - single precision floating point 4 bytes long double - double precision floating point 8 bytes long short int - signed value 2 bytes BIN(2) long int - same as int, added for compatibility BIN(4) pointer to any - 16 bytes *none * arrays and data structures are also fully supported in C * int and short int are comparable to RPG's Binary 2 and 4 * packed decimal data are not directly supported by C and must be manipulated through C arrays and external calls to conversion routines
C and APIs -> What's the Point?
Figure 3 API data structures
Figure 3: API Data Structures C structure RPG structure typedef struct header_struct { char user_data 64 ; int generic_header_size; char header_version 4 ; char format_name 8 ; char program_name 10 ; char time_generated 13 ; char informations_status; int usrspc_used; IRCVVAR DS int parm_section_offset; I B 1 40OFFSET int parm_section_size; I B 9 120NOENTR int header_section_offset; I B 13 160LSTSIZ int header_section_size; int list_section_offset; int list_section_size; int number_of_entries; int size_of_entry; } header_struct; typedef struct splf0100 { ISPLF DS char usr_name 10 ; I 1 10 USRNM1 char outq_name 10 ; I 11 20 OUTQNA char outq_lib 10 ; I 21 30 OUTQLI char form_type 10 ; I 31 40 FRMTY1 char usr_data 10 ; I 41 50 USRDT1 char int_job_ID 16 ; I 51 66 IJOBID char int_splf_ID 16 ; I 67 82 ISPLID } splf0100; typedef struct spla0100 { ISPLA DS int bytes_return; I B 1 40BYTRTN int bytes_avail; I B 5 80BYTVAL char int_job_ID 16 ; I 9 24 JOBID /* various spool file information */ I* etc. int nmbr_disk_rcdrs; I B 721 7240TOTRCD } spla0100;
C and APIs -> What's the Point?
Figure 4 C vs RPG
Figure 4: C vs. RPG Delete Old Spool Files Using C -> and Pointers +-- || create a user space using QUSRCRTU || retreive the pointer to the user space with QUSCRTU || receive spool file information into user space with QUSLSP || initialize the list section offset pointe || initialize the current entry pointer of data structure type splf010 +-- +== DO WHILE entries processed are < number of entries || retrieve more information using QUSRSPL || +--if spool file is too ol || || delete spooled fi || +- || increment current entry pointer by the list siz || increment entries processed counte +-- +-- || delete user spac +-- Delete Old Spool Files Using RPG +-- || create a user space using QUSRCRTU || receive spool files information into user space with QUSLSP || initialize list offset, entry size, number of entries fro || the user space using QUSRTVU +-- +== DO WHILE entries processed are < number of entries || retrieve one spool file entry from the user spac || to the SPLF DS with QUSRTVU || retrieve spool entry attributes to SPLA DS with QUSRSPL || +--if spool file is too ol || || delete spooled fi || +- || increment entries processed counte +-- +-- || delete user spac +--
C and APIs -> What's the Point?
Figure 5 IBM's Spoolinfo Program
Figure 5: IBM's Spoolinfo Program Important Sections of IBM's Spoolinfo C Program . . . 1 #incluse "OPENAPI.h" /* pull in API's data structures */ . . . 2 header_struct *space; /* ptr to US of type header_struct */ 3 char *list_section; /* ptr to 1st char in list section of US */ 4 splf0100 *entry_list; /* ptr to current list entry of US */ 5 spla0100 *Rcv_Spl_Var; /* ptr to Spool Attribute from QUSSPLA */ . . . 6 main(argc,argv) /* program entry parameter list */ 7 int argc; /* count of arguments/parameters */ 8 char *argv ; /* array of pointers to char */ . . . 9 QUSCRTUS(spc_name,ext_atr,initial_size,auth,desc); /* create US */ 10 QUSPTRUS(spc_name,&space); /* set space to 1st byte of US */ 11 QUSLSPL(spc_name,frmt,usr,OutQ_Nm,ls_frm_typ,Usr_dat); 12 list_section = (char *) space; /* ptr to 1st char of list in US */ 13 list_section = list_section + space->list_section_offset; 14 entry_list = (splf0100 *) list_section; /* set 1st entry ptr */ . . . 15 count = 1; 16 while (count <= space->number_of_entries) { 17 QUSRSPLA(Rcv_Var,Rcv_lgth,Rtv_Fmt,Qal_Jb-Nam, 18 entry_list->int_job_ID,entry_list->int_splf_ID, 19 Splf_Name,Splf_Number); 20 Rcv_Spl_Var = (spla0100 *) Rcv_Var; /* if spool file is old, delete it */ . 21 count++; 22 entry_list++; /* ptr arithmetic, add the base value of 1 */ /* list entry, which is the length of the */ /* data structure splf0100, so entry_list */ /* points to the next list entry */ } . . . } Important Sections of IBM's Spoolinfo RPG Program 1 IRCVVAR DS 2 I B 1 40OFFSET 3 I B 9 120NOENTR 4 I B 13 160LSTSIZ . . . 5 C CALL 'QUSCRTUS' . . . 6 C CALL 'QUSLSPL' . . . 7 C Z-ADD16 LENDTA 8 C Z-ADD125 STRPOS 9 C CALL 'QUSRTVUS' 10 C PARM USRSPC 11 C PARM STRPOS 12 C PARM LENDTA 13 C PARM RCVDTA . . . 14 C COUNT DOWLENOENTR 15 C CALL 'QUSRTVUS' 16 C PARM USRSPC 17 C PARM STRPOS 18 C PARM LENDTA 19 C PARM SPLF fig. 2 DS . . . 20 C CALL 'QUSRSPLA' * receiver parms . . . * delete spool file logic . . . 21 C ADD LSTSIZ STRPOS 22 C ADD 1 COUNT 23 C END enddo
LATEST COMMENTS
MC Press Online