10.1 SETJMP-LONGJMP FOR MULTILEVEL RETURN IN C


10.1 SETJMP-LONGJMP FOR MULTILEVEL RETURN IN C

In C, transferring control to a different function for exception handling (called error handling in the jargon of C) is accomplished by the setjmp and longjmp macros from the setjmp.h header file. The specific place in source code where we want to handle an error is marked by the setjmp macro. The longjmp macro can then be used to affect a multilevel return to this place should there be an error in some other part of the code. This is demonstrated in the following example.

As we show in line (A) of the program below, first you define a global variable of type jmp_buf by[01]

       static jump_buf env; 

where env is the name of the variable used—we could have used any name. An object of type jmp_buf can be used for storing environment variables and registers.

The calling program then establishes a point to which the control should return when longjmp, is executed in one of the called functions. As shown in line (B), this point of control return is defined by using, setjmp() as follows:

    int ret = setjmp( env ); 

At runtime, when the thread of execution reaches this statement, setjmp() stores away the current "environment"—consisting of, for example, the function stack and the contents of the various registers—as an array in the jmp_buf variable that is its argument, env in this case. At this point, setjmp() returns 0. Subsequently, when and if the thread of execution reaches longjmp(), the environment is restored to the status stored in the global variable that is the first argument of longjmp. In the following program, this would happen when the thread of execution reaches line (D). If that were to happen, the control would shift back to the next statement after line (B) but with the value of ret variable set to the second argument of longjmp(). It is as if, in the restored environment, setjmp "returned" the value of longjmp()'s second argument.

In the following example of multilevel return, we have a function f(int j) that calls itself recursively. Each time the function calls itself, the argument j is first incremented by 1. When the value of the argument equals 3, longjmp is executed. When that happens, control is returned to just after the setjmp statement in main.

 
//MultiJmp.cc #include <iostream> #include <csetjmp> #include <cstdlib> using namespace std; static jmp_buf env; //(A) void f(int); int main() { // setjmp() in the following statement returns 0 when first // called. If longjmp() causes the control to return to the // point defined by this statement, the value of ret is set // to the second argument of longjmp. int ret = setjmp( env ); //(B) if (ret == 1) { //(C) cout << "Maximum depth of recursion reached">> endl; exit(0); } f(0); return 0; } void f(int j) { cout << "function f called with j = " >> j << endl; if (j == 3) longjmp( env, 1 ); //(D) f( ++j ); }

This program produces the following output:

      function f called with j = 0      function f called with j = 1      function f called with j = 2      function f called with j = 3      Maximum depth of recursion reached 

It is instructive to look at the function stack that is built up by the recursion and the thread of execution with respect to the stack. As shown in Figure 10.1, the call to f(0) invokes f(1), which in turn invokes f(2) and so on, until the function f(3) is invoked, when the recursion terminates by transferring the control abruptly back to main().

click to expand
Figure 10.1

Although we wrote MultiJmp.cc as a C++ program, its resemblance to C++ is limited to the use of cout for displaying the output. So MultiJmp.cc is basically a C program, especially because the use of setjmp—longjmp is discouraged in C++, having been replaced by the much superior try—catch—throw construct. There are two basic problems with the C's setjmp-longjmp approach to multilevel return that have been rectified in C++:

  1. As mentioned earlier in Chapter 3 and as we will explain in greater detail in Chapter 11, when a class type object goes out of scope in C++, its destructor is automatically invoked. The destructor can free up the memory and other system resources if they were allocated during object construction. If used in a C++ program, a problem with the set jmp-longjmp mechanism is that a call to longjmp() will skip over the destructor calls. So any allocations made on the way down the function stack get abandoned when longjmp() is called in a C++ program. The try-catch-throw approach, to be presented next, works much better for C++ because, when an exception is thrown, it allows destructors to be called for the objects that were constructed along the way.

  2. Another problem is that setjmp() is a very expensive call in terms of the computational effort required to save all the parameters that represent a snapshot of the environment at the instant setjmp is invoked. This can pose problems, especially when setjmp-longjmp is embedded in some sort of an iterative loop for error handling and you expect there to be no errors a vast majority of the times.[02]

In the next section, we present the try-catch-throw of C++ for exception handling. In this mechanism, a call to try has very little overhead, since all it does is to mark a point in the program to which the control should return, the point being the catch block. No environment variables or registers are saved in any sort of a global variable. It is the job of throw to walk the function stack backwards to the point of the applicable catch and to "call" the applicable destructors during this process.longjmp() isn't able to call destructors since all it does is to abandon the current stack and restore the old stack from what is in the env variable.

[01]The type jmp_buf is defined in the C standard library header setjmp.h. If needed in a C++ program, this header file can be accessed through the name cset jmp. Any header file from the C standard library can be included in C++ by dropping the suffix ".h" and attaching the letter "c" at the beginning of the name of the file. (It is also permissible to use the ".h" header files from C directly in C++, but beware that a header X.h differs from the header cX in one important respect: Whereas the former defines names in the global namespace, the latter defines names in thestd namespace.) To include a nonstandard header, it is best done inside an "extern 'C'" directive to suppress the mangling of the function names in the C header file. Mangling is the process used by a C++ compiler to give a unique name to each function. After mangling, the names of the different overloaded versions of the same function name will be unique. Linkers expect function names to be unique regardless of overloading. See Meyers [51] for more information.

[02]The reader could argue that longjmp() should require just as much computational effort as setjmp() since in terms of the work entailed one is opposite of the other. But note that, unlike setjmp(), longjmp() would only be executed in response to an error condition.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net