Call-backs Simplify File IO
September 21, 2011 Bruce Guetzkow
Note: The code accompanying this article is available for download here.
Writing subfile programs to display a list of data elements is a task most IBM i programmers have done countless times. It can be tedious and extremely repetitive, especially if you are listing information from the same file in multiple programs. Since the logic is nearly identical in all of those programs, wouldn’t it be great if we could put some or all of it into a service program that could be used in many places?
The part of the process that is common to all of those nearly identical programs is retrieving the data from the database, so that is the part that can be off-loaded into a service program. The complete process involves each unique caller clearing the subfile, calling the new service program procedure to extract the data, and having that procedure “call back” to the caller to load the subfile. For this to work requires the use of procedure pointers.
Example 1, which you can download here, contains a service program module with a single procedure. For this example, there is a single F-spec for file MYFILE, record format MYFILER. The file is opened manually in the procedure. The “prefix” keyword is used so that input values can be associated with a data structure. If there is a need to reference any of the fields, it will be necessary to refer to them as part of qualified data structure “m”. For instance, if there is a field named “id”, it can be referenced as “m.id”. In this example there are no references to individual fields.
Prototypes are contained in a copy book named MYFILEIOPR in source file QCPYSRC. If you keep your copy books in another source file, you’ll need to change this accordingly. We’ll look at the prototypes later. Below the copy book reference are the D-specs needed to generically define any procedure that will make use of the extracted data. A Procedure Pointer is defined by specifying an asterisk (*) in column 40 along with keyword “PROCPTR”. The prototype that follows indicates that a single parameter will be passed when calling it. Instead of identifying the procedure by name, keyword “EXTPROC” is used indicating that a procedure pointer will point to the procedure to call. We’ll get to this later, too.
Next is our procedure: myfileio_readAllByPartKey. It expects two parameters: a procedure pointer and a data structure that will contain the key value(s) used to read MYFILE. The number of records read is returned to the caller upon completion. The procedure simply opens the file if not already open and reads all records with the partial key as indicated. You can modify the key data structure and number of key elements to use as needed. For each record found the record count is incremented, the procedure pointer passed in is assigned to the pointer defined above and the procedure it references is executed, passing the extracted data.
In this example I am passing the entire record for processing, but you can change that to specific fields, calculated values, etc. You could also access more than a single file, get data from data areas or anything you need.
The prototype source member (which you can find in example 2 here) has the prototype for this procedure and a few field definitions needed in the service program and any caller. The record count field is defined as 5p 0, but you can change that to anything that meets your needs.
Any program can make use of this procedure to load a subfile by creating a procedure that loads a single record (move data fields to subfile fields), incrementing the relative record number and writing the subfile record. Establish the key data structure values, then call the new procedure as indicated in example 3 (also available in the downloadable file here).
Notice that the first parameter uses the “%paddr” function to pass a procedure pointer. Now, anywhere that you want some of this same information included in a subfile, you only need to code the parts directly related to the subfile; the data extraction is already done.
You can also use this same process anywhere that a list is needed: reports, drop-down lists, etc. As long as the caller calls the “readAll” procedure passing in the proper values and the callback procedure has a prototype to accept the correct values, it will work the same way.
Bruce Guetzkow, an independent IBM i programming consultant with GmanTech Consulting in southeastern Wisconsin, is a firm believer in practical programming. For over 25 years he has developed applications for IBM systems from mainframe to System/36 to IBM i on Power. You can read his Website blog, follow him on Twitter (@gmantechi), or catch him at a meeting of the Wisconsin Midrange Computer Professional Association (WMCPA), where he is the current webmaster. Send your questions or comments for Bruce to Ted Holt via the IT Jungle Contact page.