Node:Non-Local Intro, Next:Non-Local Details, Up:Non-Local Exits
As an example of a situation where a non-local exit can be useful,
suppose you have an interactive program that has a "main loop" that
prompts for and executes commands. Suppose the "read" command reads
input from a file, doing some lexical analysis and parsing of the input
while processing it. If a low-level input error is detected, it would
be useful to be able to return immediately to the "main loop" instead
of having to make each of the lexical analysis, parsing, and processing
phases all have to explicitly deal with error situations initially
detected by nested calls.
(On the other hand, if each of these phases has to do a substantial
amount of cleanup when it exits--such as closing files, deallocating
buffers or other data structures, and the like--then it can be more
appropriate to do a normal return and have each phase do its own
cleanup, because a non-local exit would bypass the intervening phases and
their associated cleanup code entirely. Alternatively, you could use a
non-local exit but do the cleanup explicitly either before or after
returning to the "main loop".)
In some ways, a non-local exit is similar to using the return
statement to return from a function. But while return
abandons
only a single function call, transferring control back to the point at
which it was called, a non-local exit can potentially abandon many
levels of nested function calls.
You identify return points for non-local exits by calling the function
setjmp
. This function saves information about the execution
environment in which the call to setjmp
appears in an object of
type jmp_buf
. Execution of the program continues normally after
the call to setjmp
, but if an exit is later made to this return
point by calling longjmp
with the corresponding jmp_buf
object, control is transferred back to the point where setjmp
was
called. The return value from setjmp
is used to distinguish
between an ordinary return and a return made by a call to
longjmp
, so calls to setjmp
usually appear in an if
statement.
Here is how the example program described above might be set up:
#include <setjmp.h> #include <stdlib.h> #include <stdio.h> jmp_buf main_loop; void abort_to_main_loop (int status) { longjmp (main_loop, status); } int main (void) { while (1) if (setjmp (main_loop)) puts ("Back at main loop...."); else do_command (); } void do_command (void) { char buffer[128]; if (fgets (buffer, 128, stdin) == NULL) abort_to_main_loop (-1); else exit (EXIT_SUCCESS); }The function
abort_to_main_loop
causes an immediate transfer of
control back to the main loop of the program, no matter where it is
called from.
The flow of control inside the main
function may appear a little
mysterious at first, but it is actually a common idiom with
setjmp
. A normal call to setjmp
returns zero, so the
"else" clause of the conditional is executed. If
abort_to_main_loop
is called somewhere within the execution of
do_command
, then it actually appears as if the same call
to setjmp
in main
were returning a second time with a value
of -1
.
So, the general pattern for using setjmp
looks something like:
if (setjmp (buffer)) /* Code to clean up after premature return. */ ... else /* Code to be executed normally after setting up the return point. */ ...