Search code examples
csetjmp

How to safely get the return value of setjmp


I would like to return an error code using longjmp, and pass it on from the function that called setjmp. Simplified code:

int do_things(stuff ........)
{
 int error_code;
 jmp_buf jb;

 if ((error_code = setjmp(jb)) == 0) {
    /* do stuff */
    return 0;
 }
 else {
    return error_code;
 }
}

But I'v read: "An invocation of the setjmp macro shall appear only in one of the following contexts:"

 the entire controlling expression of a selection or iteration statement

if (setjmp(jb)) {
switch (setjmp(jb)) {
while (setjmp(jb)) {

or

 one operand of a relational or equality operator with the other operand
 an integer constant expression, with the resulting expression being
 the entire controlling expression of a selection or iteration statement

if (setjmp(jb) < 3) {

or

 the operand of a unary ! operator with the resulting
 expression being the entire controlling expression of a
 selection or iteration statement

if (!setjmp(jb)) {

or

 the entire expression of an expression statement (possibly cast to void).

setjmp(bf); 

Is there a nice way get the return value? ( without using switch, and writing a case for all possibly values )

EDIT

Thanks to Matt for finding it in the c99 rationale. What I came up with now, is:

int do_things(stuff ........)
{
  volatile error_code;
  jmp_buf jb;

  if (setjmp(jb) == 0) {
     working_some(&error_code, ....);
     working_again(&error_code, ....);
     working_more(&error_code, ....);
     working_for_fun(&error_code, ....);
     return 0;
  }
  else {
     general_cleanup();
     return error_code;
  }
}

One more variable, doesn't seem very nice...


Solution

  • From the C99 rationale:

    One proposed requirement on setjmp is that it be usable like any other function, that is, that it be callable in any expression context, and that the expression evaluate correctly whether the return from setjmp is direct or via a call to longjmp. Unfortunately, any implementation of setjmp as a conventional called function cannot know enough about the calling environment to save any temporary registers or dynamic stack locations used part way through an expression evaluation. (A setjmp macro seems to help only if it expands to inline assembly code or a call to a special built-in function.) The temporaries may be correct on the initial call to setjmp, but are not likely to be on any return initiated by a corresponding call to longjmp. These considerations dictated the constraint that setjmp be called only from within fairly simple expressions, ones not likely to need temporary storage.

    An alternative proposal considered by the C89 Committee was to require that implementations recognize that calling setjmp is a special case, and hence that they take whatever precautions are necessary to restore the setjmp environment properly upon a longjmp call. This proposal was rejected on grounds of consistency: implementations are currently allowed to implement library functions specially, but no other situations require special treatment.

    My interpretation of this is that it was considered to be too restrictive to specify that a = setjmp(jb); must work. So the Standard leaves it undefined. But a particular compiler may choose to support this (and hopefully, would document it). To be portable, I guess you should use some preprocessor checks to verify that the code is being compiled with a compiler that is known to support this.