08
Fri, Nov
10 New Articles

Is There an Echo in Here?

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Programs and procedures that perform general tasks are the hallmark of a well-designed computer system. An example of a general-purpose procedure is one that takes a customer number as a parameter and returns a formatted string of the customer’s name, complete with title and initials, suitable for printing on letterhead or envelopes. Of course, not every general-purpose task is so simple. Even formatting a customer name can be complex. Do you want to place the surname first or last? Should you use initials or full first names? Maybe you’d rather not precede the name with Mr. or Ms. Passing extra parameters that specify exactly how you want the name formatted improves the usefulness and generality of the procedure. But whether the task at hand is simple or complex, one thing remains the same: After you have called the procedure, you don’t want to hear from it again until it has done its job. We RPGers have become so used to this discipline that the notion of “call- back” may, at first, seem a little foreign.

Call-back is a means by which a general-purpose procedure can interrogate the waiting calling program. It does this by executing logic that resides within the caller. In some situations, call-back is superior to adding extra parameters to improve a general- purpose procedure’s usefulness. Take a look at an example of just such a situation.

A Simple Prompt Program

I have a simple window program that I call PartsPrompt. Its purpose is to present a list of automotive parts and allow a 1 to be entered against any line, indicating that the part number displayed on that line is to be returned to the caller. This program is called from dozens of interactive programs that require a part number to be entered. If users don’t know a part number, they can press F4 on the part number field to make the PartsPrompt appear. They can then either pick a part or press F3 to return. The prototype to this program (Figure 1) takes only one parameter and returns a part number.

The example code in Figure 1 shows how the PartsPrompt is used. The C-spec comes from the F4 logic of an interactive program. X_PART is the part number field displayed. Passing the current value of X_PART to PartsPrompt causes PartsPrompt to position automatically to the nearest part with a matching value to X_PART. If users select a part, the program will return that part and load X_PART with it. If users press F3 to exit PartsPrompt, the current value of X_PART will be returned.

PartsPrompt works fine when all the user wants to do is select one part, but my users asked me to beef up the program’s usefulness. I have a few subfile programs that allow entry of multiple lines of parts. When users press F4, they want to key a 1 against several parts, not just one. They also want any parts already entered in the subfile to appear in the PartsPrompt with a 1 preloaded against them. Then, if they clear the 1 in PartsPrompt, that means they want it removed from their subfile. And just to make my job interesting, they want a line to appear at the bottom of PartsPrompt that shows a running total of the cost of the parts picked so far. These demands come from the users in the service department. The guys in the retailing department want the same capability, but their running total needs to show not cost but total retail price of parts selected so far. Maybe you can think of a way to do all this using extra parameters, but I suspect you’d end up being the only person in the company able to maintain something so cumbersome. Call- back offers a more elegant solution, addressing all of these requirements while keeping the PartsPrompt relatively simple.

Need to Know

The “Need to Know” principle is one you should consider when designing general-purpose procedures. Although the requirements I had for PartsPrompt may seem complicated, just how much of it is truly necessary for the job of selecting parts? Is PartsPrompt really interested in adding up totals? Is it really interested in cost versus retail? Should it care that the caller has already selected half a dozen parts or none at all? Just what does PartsPrompt really need to know from or tell to the caller? Here’s a list of PartsPrompt’s bare necessities:
• A string that it will display at the foot of the list
• The knowledge of whether it should display a 1 against a line
• An indication that a 1 was entered against or removed from a part If PartsPrompt can call three procedures allowing it to fulfill these requirements, it can then fulfill the new requirements as well. These three procedures (getString, IsPicked, and Pick) and their prototypes are shown in Figure 2.

Now, consider how PartsPrompt uses the three procedures. The running total part is the simplest. Just prior to displaying its screen format, PartsPrompt calls getString and displays whatever it returns. This information could be cost total, retail total, or a birthday greeting. PartsPrompt doesn’t know what it’s displaying, nor does it care. Nice and easy. As for showing whether a part is already selected, PartsPrompt just calls IsPicked for every line that it’s about to display, passing the part number as a parameter. The 1 or 0 returned is what gets displayed against the line. Finally, every time the user presses the Enter key or scrolls, PartsPrompt calls Pick for every line displayed, passing both the part number and the action keyed against the line (either a 1 or 0).

PartsPrompt is now a great deal more capable than it was before and with very little coding effort required.

But wait a minute! Just where are the procedures getString, IsPicked, and Pick? Well, this is how the term call-back arose. The procedures are in the calling program. PartsPrompt is “calling back” to the program that initially called it. That’s because the calling program is in the best position to know what’s already picked and what running total should be displayed at the bottom. In effect, PartsPrompt is saying it can provide multiple selection and a running total display so long as the caller provides the getString, IsPicked, and Pick procedures. (Java programmers will recognize this principle as “implementing an interface.”)

Executing Call-back Procedures

I’ve outlined what procedures PartsPrompt needs to call to perform its new duties, and I’ve stated that these procedures reside in the calling program, but I’ve made no mention about how PartsPrompt is able to execute a procedure in the caller. This is done using procedure

pointers. Take a look at the new PartsPrompt prototype in Figure 3 and how the three call- back procedures are declared within PartsPrompt. I’ve made the three new parameters optional so that all programs currently using PartsPrompt (in single-selection mode) continue to work as before. The C-specs are the first few lines of the PartsPrompt mainline processing. These lines ensure that the rest of the program can reference IsPicked@, Pick@, and getString@ without needing to check if they were passed by the caller.

You’ll notice that the three call-back procedure prototypes have the keyword extproc defined. Extproc can take two kinds of arguments: either a string enclosed in quotation marks or the name of a procedure pointer. If the argument is a string enclosed in quotes, it is assumed to be the name of another procedure. A call to the procedure named in the prototype becomes a call to the procedure named in the extproc keyword. When the argument to extproc isn’t enclosed in quotes, as in the call-back prototypes, it is assumed to be a procedure pointer. A procedure pointer is an address, a way of identifying a procedure by its address in memory rather than by its name. You’re forced to used procedure pointers for call-back because RPG provides no useful way of passing procedure names from one program to another. Sure, you can load up a character field with the name of a procedure and pass the field, but RPG can’t execute a character field, only literal strings that explicitly name a procedure or procedures that are based on procedure pointers.

In Figure 3, you can see that the procedure IsPicked is based on the procedure pointer IsPicked@. Immediately above the prototype is the definition of IsPicked@. You define a procedure pointer by keying a * in the data type column, meaning pointer, and qualify the pointer as a procedure pointer by the keyword procptr. (If you leave out the procptr keyword, IsPicked@ is understood to be a data pointer). What this definition is saying is this: There is a procedure somewhere called IsPicked that takes a 20-alpha parameter and returns a 1-packed field. Just where IsPicked actually resides remains unknown until IsPicked@ is loaded with its address in memory.

It’s up to the calling program to pass the addresses of IsPicked, Pick, and getString to PartsPrompt. Take a look at how it does just that.

Defining Call-back Procedures

In my case, the programs calling PartsPrompt are multiline subfiles, and I’ve coded the IsPicked, Pick, and getString procedures with a subfile structure in mind. However, I’m going to show how you would code these three procedures for a calling program that stores its part numbers in an array, not a subfile. Not only is this simpler, but it also demonstrates the generality of the call-back design. Remember, PartsPrompt can do its job as long as it is provided with the addresses of the IsPicked, Pick, and getString procedures.

PartsPrompt is not concerned with the internal workings of its caller. Take a look at Figure 4 to see how this calling program defines the three procedures.

Because the logic in Figure 4 is straightforward, I won’t explain it any further. Suffice it to say that there’s not much to it (although you should note that users will encounter problems if they select more than 50 parts). All that remains is to see how the calling program tells PartsPrompt about these procedures. Figure 5 demonstrates the actual call. (Notice that, when taking the addresses of the procedures, I have to enclose their names in quotes and put them in uppercase. This formatting is necessary because all RPG symbols are stored internally in uppercase, regardless of how they were coded in the source.) The program uses the built-in function %paddr to find out the address of a procedure. By passing the addresses of IsPicked, Pick, and getString (which reside in the calling program) to PartsPrompt, PartsPrompt now knows where to locate them. While the calling program waits, PartsPrompt executes these procedures as the user scrolls through the parts list, picking and unpicking parts, with a running total being updated in real time at the foot of the screen. As soon as PartsPrompt ends and control returns to the caller, nothing more need be done. The caller’s internal array of parts has already been updated!

Gotcha!

I left one little detail out. What happens if users pick a few parts in PartsPrompt, decide they don’t want them after all, and press F3 to return to the calling program? The calling program wouldn’t be as they left it but would instead include the parts they had selected in PartsPrompt before pressing F3.

My solution to this problem is to make a copy of current parts selected in the calling program immediately prior to calling PartsPrompt. In the example, making a copy equates to a simple MOVEA statement to a backup array. If users press F3 in PartsPrompt, I return the string “F3” as the return value of PartsPrompt. (Recall that the return value of PartsPrompt is the part number selected when in single-selection mode. When you are performing a multiple selection, it’s safe to use the return value for another purpose.) If PartsPrompt returns “F3,” I then reinstate the parts that were recorded in the copy.

************************************************************

* Show list of parts, allow selection of one part only
D PartsPrompt PR 20A
D PosnToPart 20A value

C eval X_PART = PartsPrompt(X_PART)

************************************************************

Figure 1: The PartsPrompt prototype takes a single parameter and returns a part number.

************************************************************

* get a string to display at the bottom of PartsPrompt
D getString PR 80A

* Is a part already picked? Returns 1 or 0.
D IsPicked PR 1P 0
D PartNumber 20A value

* Pick/UnPick a Part.
D Pick PR
D PartNumber 20A value
D OneOrZero 1P 0 value

************************************************************

Figure 2: These procedures provide PartsPrompt with multiple-picking and running-total capability.

************************************************************

D PartsPrompt PR 20A
D PosnToPart 20A value
D OptIsPicked@ * procptr value options(*NOPASS)
D OptPick@ * procptr value options(*NOPASS)
D OptgetString@ * procptr value options(*NOPASS)

* Call-Back procedures:

D IsPicked@ S * procptr inz(*NULL)
D IsPicked PR 1P 0 extproc(IsPicked@)
D PartNumber 20A value

D Pick@ S * procptr inz(*NULL)
D Pick PR extproc(Pick@)
D PartNumber 20A value
D OneOrZero 1P 0 value

D getString@ S * procptr inz(*NULL)
D getString PR 80A extproc(getString@)

C if %parms > 1
C eval IsPicked@ = OptIsPicked@
C endif

C if %parms > 2

C eval Pick@ = OptPick@
C endif

C if %parms > 3
C eval getString@ = OptgetString@
C endif

************************************************************

Figure 3: Call-back procedures are declared within this new PartsPrompt prototype.

************************************************************

* Global definition of the parts array:
D Parts S 20A dim(50)

*----------------------------------------------------------*

P getString B
D getString PI 80A

* Locals:
D TotalCost S 9P 2 inz(0)
D I S 10I 0

C do 50 I
C if Parts(I) = *BLANKS
C eval TotalCost = TotalCost
C + RtvPartCost(Parts(I))
C endif
C enddo

C return 'Cost of parts selected so far: '
C + %trim(%editc(TotalCost:'L'))
P E

*----------------------------------------------------------*

P IsPicked B
D IsPicked PI 1P 0
D PartNumber 20A value

C PartNumber lookup Parts 01
C if *IN01
C return 1
C else
C return 0
C endif

P E

*----------------------------------------------------------*

P Pick B
D Pick PI 80A
D PartNumber 20A value
D OneOrZero 1P 0 value

* Locals:
D I S 10I 0 inz(1)

C PartNumber lookup Parts(I) 01

C select

*C when OneOrZero=0
C if *IN01
C eval Parts(I) = *BLANKS
C endif

*C when OneOrZero=1
C if not *IN01
C eval I=1
C *BLANKS lookup Parts(I) 01
C eval Parts(I) = PartNumber
C endif

*C endsl

P E

*----------------------------------------------------------*

************************************************************

Figure 4: A calling program that stores part numbers in a 50-element array defines the three call-back procedures.

************************************************************

C callp PartsPrompt(*BLANKS
C :%paddr('ISPICKED')
C :%paddr('PICK')
C :%paddr('GETSTRING'))

************************************************************

Figure 5: This is the actual call of the PartsPrompt program.

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: