l i n u x - u s e r s - g r o u p - o f - d a v i s
L U G O D
 
Next Meeting:
August 5: Social gathering
Next Installfest:
TBD
Latest News:
Jul. 4: July, August and September: Security, Photography and Programming for Kids
Page last updated:
2002 Mar 16 22:23

The following is an archive of a post made to our 'vox-tech mailing list' by one of its subscribers.

Report this post as spam:

(Enter your email address)
[vox-tech] Where setjmp/longjmp went after C
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[vox-tech] Where setjmp/longjmp went after C



The attached two files demonstrate a rudimentary "structured
setjmp/longjmp" that mimics the "exceptions" of Ada and C++ (and other
languages).  I wrote an article about this in spring 1991 for C User's
Journal, and my bad luck was that two other people beat me to the punch,
so it never got published.  What I have here is pulled from memory, but I
think it gets the idea across.

The real value of this technique comes into play when everyone uses the
same system... and you need language support to avoid certain problems
with auto variables... so I would only recommend using this if you are
stuck in C. :)

The basic idea is that you can detect certain problems deep in your
program, but if you want those pieces to be re-usable in, say, a GUI or
embedded environment, then you won't be able to report problems to the
user directly in those subroutines.  The mid- or hi level portions of the
program may already be tied into the user interface, so having them pass
on the problem to the user in an environmentally appropriate way makes
sense.  This technique is one way to propagate abnormal conditions out of
those re-usable routines.

Note that it should not be used in places where you expect to encounter
problems regularly... it is less efficient than simple tests on return
values, and you don't want all your program logic to depend on
exceptions... it gets hard to follow if you have a lot of kinds of
exceptions.  However, they are useful when you have unusual problems that
low-level routines cannot handle themselves.

j1.c is the demonstration source... jbexcept.h are the macros to be shared
throughout all source files, and j1_nomacro.c is the de-macroized j1.c so
you can follow things as they are implemented.  See the comment at the
beginning of j1.c for how to run it.

---------------------------------------------------------------------------
Jeff Newmiller                        The     .....       .....  Go Live...
DCN:<jdnewmil@dcn.davis.ca.us>        Basics: ##.#.       ##.#.  Live Go...
                                      Live:   OO#.. Dead: OO#..  Playing
Research Engineer (Solar/Batteries            O.O#.       #.O#.  with
/Software/Embedded Controllers)               .OO#.       .OO#.  rocks...2k
---------------------------------------------------------------------------
/* j1_nomacro.c - demonstrate simplified longjmp, macros removed
 */

#include <stdio.h>
/*#include "jbexcept.h"*/

jbnode *jbnode_head = ((void *)0) ;  

void f( char c )
{
  printf( "in f\n" );
  if ( 'f' == c )
    longjmp( jbnode_head->this, ( 1 ) ); ;
}

void g( char c )
{
  printf( "in g\n" );
  if ( 'g' == c )
    longjmp( jbnode_head->this, ( 2 ) ); ;
  if ( 'h' == c )
    longjmp( jbnode_head->this,( 3 ) ); ;
  printf( "return normally from g\n" );
}

void b( char volatile c )
{
  int caught=0;

  printf( "entered b with c=%c\n", c );
  {
    jbnode jbnode_local;

    jbnode_local.next = jbnode_head;
    jbnode_head = &jbnode_local;
    switch( ( *( &caught  ) ) =  _setjmp( jbnode_head->this ) ) {
    case 0: 
      {
	printf( "doing f\n" );
	f( c );
	printf( "did f\n" );
	g( c );
	printf( "did g\n" );
      }
      break;
    case ( 1 ):
      jbnode_head = jbnode_local.next;
      {
	printf( "caught MATHERR exception in b\n" );
	longjmp( jbnode_head->this, ( 2 ) );
      }
      break;
    default:
      jbnode_head = jbnode_local.next;
      {
	printf( "caught some other exception... c=%c, caught=%d\n", c, caught );
	longjmp( jbnode_head->this, ( caught ) );
      }
    }
    jbnode_head = jbnode_local.next;
  }
  printf( "leaving b\n" );
}

void a( int argc, char *argv[] )
{
  printf( "entered a\n" );
  if ( 1 < argc ) {
    
     b( argv[ 1 ][ 0 ] );  

  }
  printf( "leaving a\n" );
}

int main( int argc, char *argv[] )
{
  int caught;
  register int referenced = 1;
  
  {
    jbnode jbnode_local;

    jbnode_local.next = jbnode_head;
    jbnode_head = &jbnode_local;
    switch ( ( *( &caught ) ) = _setjmp( jbnode_head->this ) ) {
    case 0:
      {
	referenced = 2;
	a( argc, argv );
      }
      break;
    case ( 1 ) :
      jbnode_head = jbnode_local.next;
      {
	printf( "some math error detected deep in program\n" );
      }
      break;
    case ( 2 ):
      jbnode_head = jbnode_local.next;
      {
	printf( "some invalid arguments detected deep in program\n" );
      }
      break;
    default:
      jbnode_head = jbnode_local.next;
      {
	printf( "some exception raised (%d) deep in program,"
		" referenced=%d\n", caught, referenced );
      }
    }
    jbnode_head = jbnode_local.next;
  }
  printf( "end of program\n" );
  return 0;
}

/* j1.c - demonstrate simplified longjmp
 compile with -O3 to see problem with "referenced" variable
 run with single character arguments
   f : math error handled at midlevel, hi level given better explanation
   g : better explanation passed on by midlevel
   h : unexpected problem passed on by midlevel, user warned

 */

#include <stdio.h>
#include "jbexcept.h"

/* one global variable required */
jbnode *jbnode_head = NULL; /* global linkage definition */

/* f and g would in general not be using stdio to communicate
   to user */
void f( char c )
{
  printf( "in f\n" );
  if ( 'f' == c )
    JB_RAISE( EXCEP_MATHERR );
}

void g( char c )
{
  printf( "in g\n" );
  if ( 'g' == c )
    JB_RAISE( EXCEP_INVALIDARGS );
  if ( 'h' == c )
    JB_RAISE( EXCEP_UNEXPECTED );
  printf( "return normally from g\n" );
}

/* all functions could be in separate source files */

void b( char volatile c )
{
  int caught=0;

  printf( "entered b with c=%c\n", c );
  JB_TRY( &caught ) {
    printf( "doing f\n" );
    f( c );
    printf( "did f\n" );
    g( c );
    printf( "did g\n" );
  } JB_CATCH( EXCEP_MATHERR ) {
    printf( "caught MATHERR exception in b\n" );
    JB_RAISE( EXCEP_INVALIDARGS );
  } JB_CATCHDEFAULT {
    printf( "caught some other exception... c=%c, caught=%d\n", c, caught );
    JB_RAISE( caught );
  } JB_ENDTRY;
  printf( "leaving b\n" );
}

void a( int argc, char *argv[] )
{
  printf( "entered a\n" );
  if ( 1 < argc ) {
    
     b( argv[ 1 ][ 0 ] ); /* notice that this function need
			     not even be aware of JB_TRY */
  }
  printf( "leaving a\n" );
}

int main( int argc, char *argv[] )
{
  int caught;
  register int referenced = 1; /* unsafe definition */
  /* int volatile referenced = 1; correct definition - forces variable into memory */
  
  JB_TRY( &caught ) {
    referenced = 2;
    a( argc, argv );
  } JB_CATCH( EXCEP_MATHERR ) {
    printf( "some math error detected deep in program\n" );
  } JB_CATCH( EXCEP_INVALIDARGS ) {
    printf( "some invalid arguments detected deep in program\n" );
  } JB_CATCHDEFAULT {
    printf( "some exception raised (%d) deep in program,"
	    " referenced=%d\n", caught, referenced );
  } JB_ENDTRY;
  printf( "end of program\n" );
  return 0;
}

/* jbexcept.h - simplified use of setjmp/longjmp
   by Jeff Newmiller
   This is not as robust as C++ exceptions are, because
   auto variables optimized into registers aren't
   restored after the longjmp/RAISE.
 */

#ifndef JBEXCEPT_H
#define JBEXCEPT_H

#include <setjmp.h>

typedef struct jbnode_t {
  struct jbnode_t *next;
  jmp_buf this;
} jbnode;

extern jbnode *jbnode_head;

#define JB_LN jbnode_local.next=jbnode_head;jbnode_head=&jbnode_local;
#define JB_ULN jbnode_head=jbnode_local.next;
#define JB_TRY(v) {jbnode jbnode_local;JB_LN;switch((*(v))=setjmp(jbnode_head->this)){case 0:
#define JB_CATCH(v) break;case (v):JB_ULN;
#define JB_CATCHDEFAULT break;default:JB_ULN;
#define JB_ENDTRY }JB_ULN}
#define JB_RAISE(v) longjmp(jbnode_head->this,(v));

#define EXCEP_MATHERR 1
#define EXCEP_INVALIDARGS 2
#define EXCEP_UNEXPECTED 3

#endif  /* JBEXCEPT_H */


LinkedIn
LUGOD Group on LinkedIn
Sign up for LUGOD event announcements
Your email address:
facebook
LUGOD Group on Facebook
'Like' LUGOD on Facebook:

Hosting provided by:
Sunset Systems
Sunset Systems offers preconfigured Linux systems, remote system administration and custom software development.

LUGOD: Linux Users' Group of Davis
PO Box 2082, Davis, CA 95617
Contact Us

LUGOD is a 501(c)7 non-profit organization
based in Davis, California
and serving the Sacramento area.
"Linux" is a trademark of Linus Torvalds.

Sponsored in part by:
EDGE Tech Corp.
For donating some give-aways for our meetings.