There are no notfications.

Nonlocal jumps

The setjmp.h header provides the means to bypass the normal function call and return discipline through nonlocal jumps whilst preserving the calling environment. This may be used to implemented exception handling and cooperative multitasking (coroutines). By contrast, the keyword goto performs local jumps.

Example 1

The following short example demonstrates the fundamentals of nonlocal jumps. The example is contrived for the sake of simplicity.

Code

#include <setjmp.h> // "jmp_buf", "longjmp" and "setjmp".
#include <stdio.h> // "puts".
#include <stdlib.h> // "exit".
#include <stdnoreturn.h> // "noreturn".

noreturn void perform_jump(jmp_buf calling_environment);

int main(void)
{
	puts("main: begin");

	jmp_buf calling_environment; // Array (macro) which will contain information about the calling environment.

	if (setjmp(calling_environment)) // Invoking "setjmp" function-like macro results in the calling environment being saved to "env". When invoked directly, "setjmp" always returns 0.
	{
		exit(0);
	}

	perform_jump(calling_environment);

	puts("main: end"); // Unreachable code.

	return 0; // Unreachable code.
}

noreturn void perform_jump(jmp_buf calling_environment)
{
	puts("perform_jump: begin");

	longjmp(calling_environment, 1); // Long jump is performed by restoring the calling environment back to when "setjmp" was directly invoked. 1 is returned by "longjmp" to "setjmp" which in turn returns the same value.

	puts("perform_jump: end"); // Unreachable code.
}

Execution

Standard output stream:

main: begin
perform_jump: begin

Explanation

  1. Line 10: puts outputs "main: begin".
  2. Line 14: A direct invocation of setjmp is performed and calling_environment is passed as an argument. The calling environment is saved to calling_environment and 0 is returned. if (0) evaluates to false, thus the block is not entered.
  3. Line 19: perform_jump is invoked.
  4. Line 28: puts outputs "perform_jump: begin".
  5. Line 30: An invocation of longjmp is performed and calling_environment is passed as an argument along with 1. The calling environment stored in calling_environment is restored and 1 is returned.
  6. Line 14: setjmp returns 1 obtained from longjmp. if (1) evaluates to true, thus the block is entered.
  7. Line 16: exit is invoked.

Example 2

The following slightly longer example demonstrates the fundamentals of nonlocal jumps. The example is also contrived for the sake of simplicity.

Code

#include <setjmp.h> // "jmp_buf", "longjmp" and "setjmp".
#include <stdio.h> // "puts".
#include <stdnoreturn.h> // "noreturn".

jmp_buf calling_environment; // Array (macro) which will contain information about the calling environment.

noreturn void first(void);
noreturn void second(void);

int main(void)
{
	puts("main: begin");

	if (!setjmp(calling_environment)) // Invoking "setjmp" function-like macro results in the calling environment being saved to "calling_environment". When invoked directly, "setjmp" always returns 0.
	{
		first();
	}

	puts("main: end");

	return 0;
}

noreturn void first(void)
{
	puts("first: begin");

	second();

	puts("first: end"); // Unreachable code.
}

noreturn void second(void)
{
	puts("second: begin");

	longjmp(calling_environment, 1); // Long jump is performed by restoring the calling environment back to when "setjmp" was directly invoked. 1 is returned by "longjmp" to "setjmp" which in turn returns the same value.

	puts("second: end"); // Unreachable code.
}

Execution

Standard output stream:

main: begin
first: begin
second: begin
main: end

Explanation

  1. Line 12: puts outputs "main: begin".
  2. Line 14: A direct invocation of setjmp is performed and calling_environment is passed as an argument. The calling environment is saved to calling_environment and 0 is returned. if (!0) evaluates to true, thus the block is entered.
  3. Line 16: first is invoked.
  4. Line 26: puts outputs "first: begin".
  5. Line 28: second is invoked.
  6. Line 35: puts outputs "second: begin".
  7. Line 37: An invocation of longjmp is performed and calling_environment is passed as an argument along with 1. The calling environment stored in calling_environment is restored and 1 is returned.
  8. Line 14: setjmp returns 1 obtained from longjmp. if (!1) evaluates to false, thus the block is not entered.
  9. Line 19: puts outputs "main: end".
  10. Line 21: main returns 0, indicating successful execution.

Example 3

Exception handling; akin to that of C++, C# and Java; can be variously implemented in C using setjmp.h. Futhermore, by using macros, the try–catch–finally structure can additionally be mimicked. The C programming language has hitherto relied on error codes for error detection and correction. It is not considered good practice to rely on such an implementation as it can have unforeseen consequences and other developers will not be familiar with the particularities of such an implementation and its paradigm.

The following example demonstrates an implementation of exception handling. The example should only be considered a novelty.

Code

exception_handling.h

#ifndef EXCEPTION_HANDLING
#define EXCEPTION_HANDLING

#include <setjmp.h> // "jmp_buf", "longjmp" and "setjmp".

#define TRY do { jmp_buf calling_environment; switch (setjmp(calling_environment)) { case 0: while(1) {
#define CATCH(exception) break; case exception:
#define FINALLY break; } default: {
#define TRY_END break; } } } while(0)

#define THROW(exception) longjmp(calling_environment, exception)

#endif

main.c

#include <stdio.h> // "puts".
#include "exception_handling.h" // "CATCH", "FINALLY", "THROW", "TRY" and "TRY_END".

#define FIRST_EXCEPTION 1
#define SECOND_EXCEPTION 2
#define THIRD_EXCEPTION 3

int main(void)
{
	int exit_value = 0;

	TRY
	{
		puts("TRY: begin");

		THROW(SECOND_EXCEPTION);

		puts("TRY: end"); // Unreachable code.
	}
	CATCH (FIRST_EXCEPTION)
	{
		puts("FIRST_EXCEPTION");

		exit_value = FIRST_EXCEPTION;
	}
	CATCH (SECOND_EXCEPTION)
	{
		puts("SECOND_EXCEPTION");

		exit_value = SECOND_EXCEPTION;
	}
	CATCH (THIRD_EXCEPTION)
	{
		puts("THIRD_EXCEPTION");

		exit_value = THIRD_EXCEPTION;
	}
	FINALLY
	{
		puts("FINALLY");
	}
	TRY_END;

	return exit_value;
}

Execution

Standard output stream:

TRY: begin
SECOND_EXCEPTION
FINALLY

Explanation

The exception_handling.h file contains unintuitive macros that map the keywords of the trycatchfinally structure to loops, a switch statement and the elements of the setjmp.h header.

The main.c file is assumed to perform some function and then return (exit) with a value indicating success (0) or failure (nonzero). At some point, the TRY block is entered where code that can fail in a few different ways is executed. An exception is thrown and caught by the relevant CATCH block. The integer representing the exception is stored in exit_value for later use. The optional FINALLY block is entered where resource cleanup takes place.