So, how could life be better? You answered that question, whether you realize it or not, when you made a choice early in the application design stage that later proved to be either completely wrong at worst or inflexible at best. To make the seriousness of such choices painfully clear, it sometimes takes a shift like the one from DB2 to UDB/400, for example. UDB/400 facilitates the creation and access of files with null-capable fields, which is great--unless your applications are not set up to handle null-field processing. This is when life could be significantly better. But how?
ILE Is the Answer
The answer is provided by ILE. C I/O functions have
the flexibility to free us from the shackles of both static file declaration
choices and the lack of support for runtime file selection containing null-field
data. Note that I'm talking about support for null-field data in
program-described files, which is not the same as support for null fields in
externally described files (currently offered only as a compile option). You
might challenge my assertion that there's a need to use C I/O in the normal
course of application development--except in rare instances, like a generic file
access or reporting utility, where runtime overrides are made to an internal
program-described file. But the premise put forth in this article is that
flexibility is gained in all development activities by using C I/O instead of
RPG opcodes.
A Shift in Thinking
The complete code that
accompanies this article is available for download.
Read on to learn about it.
Figure 1 shows the shell of a new I/O paradigm
for RPG IV (ILE RPG) that employs C I/O.
|
ior()
Arguments:
1:
First integer to be
ORed
2: Second
integer to be
ORed
Return:
Combined (ORed) integer result
|
is_field_null()
Arguments:
1:
Pointer to null map (innullmap or
outnullmap)
2:
Length of null
map
3: Field number
(order) in file to be tested for
nulls
Return:
Boolean
(true = field contains null value or false = not null)
|
set_null_map()
Arguments:
1:
Pointer to null map (innullmap or
outnullmap)
2:
Length of null
map
3: Null
indicator value ('0' = *off or not null, '1' = *on or
set to null)
(optional, defaults
to *off)
4: Field
number (order) in file to be set to null (optional, defaults to all
fields)
Return:
Pointer
to file ODP
|
*------- File pointer positioning constants d ropen_max c 32766 d rrn_eq c x'08000300' d key_eq c x'0B000100' d key_gt c x'0D000100' d key_lt c x'09000100' d key_le c x'0A000100' d key_ge c x'0C000100' d key_nxtunq c x'05000100' d key_prvunq c x'06000100' d key_nxteq c x'0E000100' d key_prveq c x'0F000100' d first c x'01000300' d last c x'02000300' d next c x'03000300' d previous c x'04000300' d start_frc c x'03000004' d start c x'01000004' d end_frc c x'04000004' d end c x'02000004' *------- Record locking and IO feedback buffer option constants d dft c x'0B000100' d no_lock c x'00000001' d no_posn c x'00100000' d prior c x'00001000' d data_only c x'00000002' d nulkey_map c x'00000008' d read_next c 3 d read_prev c 4 *------- Null capable field option values (I/O) d notnul_val c '0' d nul_val c '1' d map_error c '2' d dk_yes c 1 d dk_no c 0 *------- IO operation potential errors d etrunc c 3003 d enotopen c 3004 d enotread c 3005 d erecio c 3008 d enotwrite c 3009 d enorec c 3026 d enotupd c 3041 d enotdlt c 3042 d eioerror c 3101 d eiorecerr c 3102 d eof s 10i 0 inz(-1) d rc s 10i 0 d errnum s 10i 0 based(perrnum) d ds d nopts 10i 0 d aopts 4a overlay(nopts) *------------------------------------------------------------------ * I/O record access feedback data structures *------------------------------------------------------------------ d riofb_t ds based(rfb) d key@ * d sysparms * d rrn 10u 0 d num_bytes 10i 0 d blk_count 5u 0 d blk_fillby 1a * dup_key(bit 1) + icf_locate(bit 2) + resrvd1(bits 3-8) * d mixed_bits 1a d resrvd2 20a d rfile ds based(fp) d reserved1 16a d in_buf * d out_buf * d reserved2 48a d riofb@ like(riofb_t) d reserved3 32a d buf_length 10u 0 d reserved4 28a d innullmap * d outnullmap * d nullkeymap * d reserved5 48a d min_length 10i 0 d nullmaplen 5u 0 d nullkmapln 5u 0 d reserved6 8a
|
*================================================================= * I * MCPressOnline I Recioh - RPG prototypes for* I * I * I Note: Unless otherwise specified, most every * I character string must be null-terminated. * I Add an 'f' suffix to function prototypes * I names to avoid ambiguity with free-form * I RPGIV opcodes. *================================================================= *------------------------------------------------------------------ * Prototype for _Riofbk(). *------------------------------------------------------------------ d iofbk pr * ExtProc('_Riofbk') d * value *------------------------------------------------------------------ * Prototype for _Ropen(). *------------------------------------------------------------------ d open pr * ExtProc('_Ropen') d * value d * value *------------------------------------------------------------------ * Prototype for _Ropnfbk(). *------------------------------------------------------------------ d openfbk pr * ExtProc('_Ropnfbk') d * value *------------------------------------------------------------------ * Prototype for _Rlocate(). *------------------------------------------------------------------ d locate pr * ExtProc('_Rlocate') d * value d * value d 10i 0 value d 10i 0 value *------------------------------------------------------------------ * Prototype for _Rreadk(). *------------------------------------------------------------------ d readk pr * ExtProc('_Rreadk') d * value d * value d 10i 0 value d 10i 0 value d * value d 10u 0 value *------------------------------------------------------------------ * Prototype for _Rreadn(). *------------------------------------------------------------------ d readn pr * ExtProc('_Rreadn') d * value d * value d 10i 0 value d 10i 0 value options(*nopass) *------------------------------------------------------------------ * Prototype for _Rreadd(). *------------------------------------------------------------------ d readd pr * ExtProc('_Rreadd') d * value d * value d 10i 0 value d 10i 0 value d 10i 0 value *------------------------------------------------------------------ * Prototype for _Rreadf(). *------------------------------------------------------------------ d readf pr * ExtProc('_Rreadf') d * value d * value d 10i 0 value d 10i 0 value options(*nopass) *------------------------------------------------------------------ * Prototype for _Rreadl(). *------------------------------------------------------------------ d readl pr * ExtProc('_Rreadl') d * value d * value d 10i 0 value d 10i 0 value options(*nopass) *------------------------------------------------------------------ * Prototype for _Rreadp(). *------------------------------------------------------------------ d readp pr * ExtProc('_Rreadp') d * value d * value d 10i 0 value d 10i 0 value options(*nopass) *------------------------------------------------------------------ * Prototype for _Rupdate(). *------------------------------------------------------------------ d update pr * ExtProc('_Rupdate') d * value d * value d 10i 0 value *------------------------------------------------------------------ * Prototype for _Rwrite(). *------------------------------------------------------------------ d write pr * ExtProc('_Rwrite') d * value d * value d 10i 0 value *------------------------------------------------------------------ * Prototype for _Rdelete(). *------------------------------------------------------------------ d delete pr * ExtProc('_Rdelete') d * value *------------------------------------------------------------------ * Prototype for _Rclose(). *------------------------------------------------------------------ d close pr 10i 0 ExtProc('_Rclose') d * value *------------------------------------------------------------------ * Prototype for errno(). *------------------------------------------------------------------ d errno pr * ExtProc('__errno') *------------------------------------------------------------------ * Prototype for clearerr(). *------------------------------------------------------------------ d clearerr pr ExtProc('clearerr') d * value
|
open()
Arguments:
1:
Pointer to file name
string
2: Pointer to
open
options
Return:
Pointer to file ODP
locate()
Arguments:
1:
File pointer (fp)
2:
Pointer to key data or null
pointer
3: Length of
key data or 0 when null
pointer
4: Record
processing
options
Return:
Pointer
to record feedback area (rfb)
|
readn()
Arguments:
1:
File pointer (fp)
2:
Pointer to data buffer format (buffer for customer record)
3: Size of customer
record buffer
4:
Record processing
options
Return:
Pointer
to record feedback area (rfb)
|
close()
Arguments:
1:
File pointer
(fp)
Return:
Return
code = 0 if successful, eof (–1) if unsuccessful
|
Figure 1: This I/O paradigm for RPG uses C I/O.
Close
examination of the /Copy members recioth and recioh ([A] and [B], respectively,
in Figure 1) reveals the type definitions and prototypes (shown, in italicized
code fragments immediately after the copy member statements) required to
effectively use C I/O in an RPG IV application or function. Stepping through a
few of these will demonstrate their purpose and ease of use. There are a few
steps that, if you understand them well, will help you use C I/O with the same
confidence and proficiency as you currently use RPG I/O opcodes. They are listed
below:
1. Declare Files
Ensure the file name string variable (ofile in [E])
is properly initialized with the desired file name (either fully qualified to
the exact library/file [member] or defaulted to *LIBL) and null-terminated (as
shown in the top portion of [G]). In this case, the file name is initialized to
"*LIBL/CUSTOMER(*first)".
2. Set Access Options
Ensure the file open options (oopts in [E]) are
null-terminated (as shown in the top portion of [G]) and properly initialized
with your desired access requirements. In this case, rr means read-only access;
for other options, refer to the ILE C/C++ Run-Time Library Reference
(SC09-2715-00).
3. Set Processing Options
Ensure your file processing options are properly
defined (recioth in [A]) and combined in the proper manner to enable your
desired access. To combine the file processing options, they can be ORed as
shown in [F] of Figure 1, created with the ior function written in C as shown in
Figure 2 below, or written in RPG IV as shown in Figure 3 below.
4. Specify C I/O Function Calls for Desired Processing
Using the file name string and file processing
options integer variables defined in previous steps and the declared C I/O
prototypes (declared in recioh and shown throughout Figure 1 in the [B] labels),
make the following calls:
Open (_Ropen) the desired file
and ensure the file pointer (fp in [E]) is not *null (as shown in [G]).
Otherwise, send an error message using the printf function as shown in a
previous article, "What?
RPG IV a Better C Than C?", and prototyped in [C].
Position
(_Rlocate) the file cursor where desired (using either an appropriate key value
string null-terminated or the equivalent of *loval as shown in [H]) and verify
successful positioning by ensuring the num_bytes variable is zero. Otherwise,
send an error message with the printf function and end the
program.
Loop and read (_Rreadn) records from the file (pointed to
by fp) into the buffer (pointed to by taking the %addr of buffer redefined by
your file's external definition--in this case, Customer as shown in [D]--for the
%size of buffer as shown in [I]).
Close (_Rclose) the file
(pointed to by fp) and set the pointer to *null if your program is reentrant (as
shown in [J]). Then, end the program.
5. Implement Proper Error Handling
Use language-independent condition handlers (as
implemented in [K1] and [K2]) and check C record feedback fields (as defined in
[H] for the locate operation and [I] for the read operation) to ensure file
conditions are detected and responded to in the proper manner.
Those are
the basic steps you need to know to effectively use C I/O functions in your RPG
IV applications.
|
Figure 2: This is the "C inclusive OR" (CIOR) utility.
|
Figure 3: This is the "RPG inclusive OR" (RPGIOR) function.
By Way of Example
The example program of Figure 1 reads through the
Customer file and prints the customer name, city, and state, double-spaced to
the display. When the program encounters a customer with a null value for the
discount field, it will print an additional line to indicate so.
An Inclusionary OR?
In addition, to "OR" the file access options
together, rather than using the ior function (as implemented in Figure 2 with
the "|", which requires a C compiler), you can instead implement the code shown
in Figure 3 as an RPG IV subprocedure and use the same prototype shown at the
top of Figure 1. It is a bit clumsy, comparatively speaking, and can be replaced
in V5R2 with the more elegant %OR BIF that one could safely assume will permit a
simple format like the following:
This would allow you to replace the code in [F] of Figure 1 like
so:
eval @setll = %or(@setll : @prior)
This is much more satisfying than maintaining the bulkier RPG
subprocedure depicted in Figure 3 or resorting to another language, as depicted
in Figure 2, to obtain the required functionality.
Managing Your Null-Capable Fields
Null field values can be detected by checking the
field null indicator array defined in recioth in the file information data
structure (rfile in Figure 1, adjacent to [A]), pointed to by the file pointer
fp, and identified by the pointers innullmap and outnullmap, depending on the
type of operation executed. The innullmap is interrogated on an input operation
when you want to determine if the field currently in the record buffer is a null
value or not (a null field will be set to the default value for the field data
type, *blanks for alpha and *zeros for numeric, in the record buffer). The
outnullmap should be set just prior to writing a record, but the innullmap
should be set just prior to updating a record. Set the field array indicator to
*off when the relative field value to be updated is not null, and set the field
array value to *on when you want to set the relative field value to *nulls.
Also shown in [I] of Figure 1 is the use of
the is_field_null prototyped in [L] of Figure 1 and shown in its entirety in
Figure 4.
|
Figure 4: This is an example of null map handling in RPG
IV.
This function takes three arguments (a pointer to a file field
null map, the length of the null map, and an ordinal integer value for
representing the field in the file to test for nulls). It returns a Boolean
value of true or false, depending on whether or not the field identified in the
third argument contains nulls. Note that the pointer is passed by value to this
function rather than used as a basing pointer in the main procedure of the
program. The reason for this is not only for the purpose of efficiency and reuse
of logic, but also because the null map pointers (input used on read-only and
update access; output used on writes) are defined in the file information data
structure (rfile in Figure 1, depicted in the window adjacent to [A]) as const *
(or constant pointers). This ensures that the pointer cannot be changed (or
referred) directly, or used as a basing pointer, without providing one level of
indirection (or a copy of the pointer rather than the actual pointer itself).
The is_field_null and the set_field_null functions, shown in Figure 4,
are defined as much for functionality as for practicality. This point can go
unnoticed if you are using the C I/O functions within the scope of a C program,
because all arguments are passed by value to C functions. So you would not
experience any problems in referring to the pointer directly, as it is a copy
and not the actual pointer.
Handling the Unexpected
The registration and de-registration of a condition
handler prototyped in [K1] and defined in [K2] is shown in [I]. The condition
handler allows the programmer to pass a reference to a Boolean (n-data type) or
a program indicator that will be set on in the event of an I/O exception during
the read and allows the program to resume without causing an abnormal exception
condition to be raised. Additionally, the num_bytes variable (defined in the
recioth riob_t data structure, pointed by rfb, and returned in both the locate
and read operations) is interrogated for equivalence to eof (an integer defined
in recioth with a value of –1) to determine if an end-of-file condition
has been reached. You can also provide more granular error handling by
retrieving the error number (using the __errno C function) and printing the
error (using the perror C function) and/or by interrogating the condition token
fields shown in [K2]. However, that is beyond the topic of discussion in this
article.
Another unexpected situation may arise if you are using the
included C I/O prototypes in free-form ILE RPG IV style. For example, if you mix
the C I/O prototype close() in free-form ILE RPG, the compiler will not be able
to implicitly resolve the ambiguity of having two close operations: the C I/O
close() and the RPG IV free-form close(). You see, in free-form RPG IV, the
close operation has an implicit first argument that allows specification of the
operational extender in free-form style, like so: close(E). To resolve this
ambiguity, rename your C I/O prototypes by adding an "f" suffix character to
each function, like so:
d file_pointer * value
So remember: To avoid ambiguities when using free-form RPG IV, always add
a suffix character to C I/O operations to distinguish them from free-form RPG IV
I/O operations.
Give It a Whirl, and Stay Tuned to MC Mag Online
As I mentioned earlier, the complete code for this
article can be downloaded from the MC Press Web site. It contains the SQL source
statements necessary to create and populate the Customer file with records to
test this application on your system. Notwithstanding your current application
requirements, future applications will be better leveraged to take advantage of
opportunities posed by industrious users, and your response time in meeting new
feature requirements will be considerably improved by using C I/O rather than
RPG I/O opcodes. This leaves you with more time to read the next article in this
series, "Interfaces in ILE RPG IV--Finding the Middle Ground," which describes
implementing interfaces in RPG IV and is accompanied by a Java source program to
keep you awake! How could life be better?
Jim Barnes is a freelance writer and independent consultant working in Houston, Texas. Jim can be reached at
LATEST COMMENTS
MC Press Online