home *** CD-ROM | disk | FTP | other *** search
- /*
- #### # # # #
- # # # # # The FreeWare C library for
- # # ## ### # # # # ### RISC OS machines
- # # # # # # # # # # # ___________________________________
- # # #### ### ## # # # #
- # # # # # # # # # # Please refer to the accompanying
- #### ### #### # # ##### # ### documentation for conditions of use
- ________________________________________________________________________
-
- File: Jump.h
- Author: Copyright © 1995 Julian Smith
- Version: 1.00 (26 Jan 1996)
- Purpose: Provides try..throw..catch system without pulling in DeskLib's
- Error2 header etc (unless compiling for SDLS.
- */
-
- #ifndef __Desk_JumpRaw_h
- #define __Desk_JumpRaw_h
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- #include <setjmp.h>
-
-
- #ifndef __Desk_Debug_h
- #include "Desk.Debug.h"
- #endif
-
-
-
- /*
- This header defines two sets of macros, Desk_Jump_* and Desk_JumpAuto_* . There
- are rather a lot of macros, so that you can choose to use them in the
- way you want, rather than be forced to write things in a particular way.
-
- Probably the most useful are the three Desk_JumpAuto_Try, Desk_JumpAuto_Catch and
- Desk_JumpAuto_EndCatch macros, or the Desk_JumpAuto_TryCatch macro.
-
-
-
- Desk_Jump_ macros
- ------------
-
- The macros Desk_Jump_* are for SDLS-happy set/longjmp-ing. They behave like
- setjmp / longjmp etc in normal compilation, but do a few extra things
- when compilation is for a SDLS, because the SDLS needs to know about
- long-jmping in order to keep an internal stack up-to-date.
-
- See Desk_Jump_SetJmp for more information.
-
-
-
-
- Desk_JumpAuto_ macros
- ----------------
-
- The macros Desk_JumpAuto_* can be used in a similar way, except that they
- also automatically define and use local variables to effectively
- maintain a stack of jmp_buf's. This allows a function to call
- 'Desk_JumpAuto_Throw( val) instead of Desk_Jump_LongJmp( buf, val), ie without
- having been passed a jmp_buf explicitly.
-
- This means you don't have to pass a (jmp_buf *) to every function which
- might want to call 'longjmp'.
-
-
- To make things even simpler, you can use the Desk_JumpAuto_Try,
- Desk_JumpAuto_Catch, Desk_JumpAuto_EndCatch or Desk_JumpAuto_TryCatch macros.
-
- */
-
-
-
-
-
-
- /* Desk_Jump_* */
-
- typedef struct {
- jmp_buf jmpbuf;
-
- #ifdef Desk__using_SDLS
- int Desk_jump__sdls_stackptr;
- #endif
- }
- Desk_jump_buf;
- /*
- This is identical to the normal jmp_buf when compilation is for static
- linking. With SDLS, an extra field is present which is needed by the
- SDLS.
- */
-
-
-
- #ifdef Desk__using_SDLS
-
- #include "Desk.Core.h"
-
- #define Desk_Jump_SetJmp( buf) ((buf).Desk_jump__sdls_stackptr=_dll_setjmp(), setjmp( (buf).jmpbuf))
- /*
- int Desk_Jump_SetJmp( Desk_jump_buf buf);
- */
-
- #define Desk_Jump_ReceiveLongJmp( buf) _dll_longjmped( (buf).Desk_jump__sdls_stackptr)
- /*
- void Desk_Jump_ReceiveLongJmp( Desk_jump_buf buf);
- */
-
- #else
-
- #define Desk_Jump_SetJmp( buf) setjmp( (buf).jmpbuf)
- /*
- int Desk_Jump_SetJmp( Desk_jump_buf buf);
-
- SDLS-compatible version of 'setjmp'.
-
- Instead of:
-
- jmp_buf buf;
- void Bar( void) { if (error) longjmp( buf, 1); }
- void Foo( void)
- {
- if ( !setjmp( buf)) { ... Bar(); ... }
- else |* Handler error *| }
- }
-
- do:
-
- Desk_jump_buf buf;
- void Bar( void) { if (error) Desk_Jump_LongJmp( buf, 1); }
- void Foo( void)
- {
- if ( !Desk_Jump_SetJmp( buf)) { ... Bar(); ... }
- else {
- Desk_Jump_ReceiveLongJmp( buf);
- |* Handle error *|
- }
- }
-
- Ie. Desk_Jump_SetJmp and Desk_Jump_LongJmp are drop-in replacements for setjmp
- and longjmp. Desk_Jump_ReceiveLongJmp() should be called immediately after a
- longjmp happens, and Desk_jump_buf should be used instead of jmp_buf, to
- define a jmp_buf.
- */
-
- #define Desk_Jump_ReceiveLongJmp( buf)
- /*
- Macro to call when a longjmp is received, for compatibility with SDLS.
-
- See Desk_Jump_SetJmp.
- */
-
- #endif
-
-
- #define Desk_Jump_LongJmp( buf, val) longjmp( (buf).jmpbuf, val)
- /*
- void Desk_Jump_LongJmp( Desk_jump_buf buf, int val);
-
- SDLS-compatible version of 'setjmp'.
-
- Actually, as you can see, this macro is identical in SDLS and non-SDLS
- builds. It is included so that one can use 'Desk_Jump_' macros for
- everything.
-
- See Desk_Jump_SetJmp.
- */
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- /* Desk_JumpAuto_* */
-
- typedef struct Desk_jumpauto_buf {
- Desk_jump_buf jumpbuf;
- struct Desk_jumpauto_buf* previous;
- }
- Desk_jumpauto_buf;
- /*
- This contains all information needed to allow the JumpAuto system to
- keep track of a stack of Desk_jump_buf's.
- */
-
-
- #ifdef Desk__using_SDLS
- extern Desk_jumpauto_buf** Desk_Jump__Ref_autonewestbuf( void);
- #endif
-
- #if defined( Desk__using_SDLS) && !defined( Desk__making_Jump)
- #define Desk_jumpauto_newestbuf (*Desk_Jump__Ref_autonewestbuf())
- #else
- extern Desk_jumpauto_buf *Desk_jumpauto_newestbuf;
- /*
- Always points to the most recent Desk_jumpauto_buf. Is NULL initialy.
- */
- #endif
-
-
- #define Desk_JumpAuto__EscapeFromNestedTryUsingReturnOrBreak() \
- (Desk_jumpauto_newestbuf!=&Desk_jumpauto__localbuf)
- /*
- A macro that detects list corruption (probably caused by breaking or
- returning from a try block).
- */
-
-
- #define Desk_JumpAuto__Push( jumpautobuf) \
- (jumpautobuf).previous = Desk_jumpauto_newestbuf; \
- Desk_jumpauto_newestbuf = &(jumpautobuf)
- /*
- This makes subsequent Desk_JumpAuto_LongJmp's go to 'jumpautobuf', and stores
- where they were originally going, so that this place can be restored
- later.
- */
-
- #define Desk_JumpAuto__Pop( jumpautobuf) \
- Desk_jumpauto_newestbuf = (jumpautobuf).previous
- /*
- Makes subsequent Desk_JumpAuto_LongJmp's go to whatever was the previous
- Desk_JumpAuto_SetJmp point (usually this will be in a parent function of the
- present function).
- */
-
-
- #define Desk_JumpAuto_Try \
- { \
- Desk_jumpauto_buf Desk_jumpauto__localbuf; \
- volatile int Desk_jumpauto_val; \
- Desk_JumpAuto__Push( Desk_jumpauto__localbuf); \
- Desk_jumpauto_val = Desk_Jump_SetJmp( Desk_jumpauto__localbuf.jumpbuf); \
- /*Desk_Debug_PrintMemory( Desk_jumpauto_newestbuf, sizeof( Desk_jump_buf));*/ \
- if (!Desk_jumpauto_val) {
- /*
- See Desk_JumpAuto_TryCatch
- */
-
- #define Desk_JumpAuto_Catch \
- /* Clean up after try code has finished succesfully: */ \
- /*Desk_Debug_Assert( !Desk_JumpAuto__EscapeFromNestedTryUsingReturnOrBreak());*/ \
- Desk_JumpAuto__Pop( Desk_jumpauto__localbuf); \
- } \
- else { \
- /* Clean up after a longjmp: */ \
- Desk_JumpAuto__Pop( Desk_jumpauto__localbuf); \
- Desk_Jump_ReceiveLongJmp( Desk_jumpauto__localbuf.jumpbuf);
- /*
- See Desk_JumpAuto_TryCatch
- */
-
-
- #define Desk_JumpAuto_EndCatch \
- } \
- }
- /*
- See Desk_JumpAuto_TryCatch
- */
-
-
-
- #define Desk_JumpAuto_Throw( val) Desk_Jump_LongJmp( Desk_jumpauto_newestbuf->jumpbuf, val)
- /*
- Behaves like longjmp, jmping to the most recent Desk_JumpAuto_SetJmp place.
- 'val' could be a pointer to an error structure such as an Desk_error2_block
- (cast to an int). Note that this structure must not be on the stack.
-
- It is up to the receiving code to call Desk_JumpAutp__Pop, so that subsequent
- Desk_JumpAuto_Throw's go somewhere sensible.
- */
-
-
-
- #define Desk_JumpAuto_TryCatch( trycode, catchcode) \
- Desk_JumpAuto_Try { trycode } \
- Desk_JumpAuto_Catch { catchcode } \
- Desk_JumpAuto_EndCatch
- /*
- Purpose
- -------
- This macro, along with Desk_JumpAuto_Throw, allows you to write pseudo
- try...catch code without worrying about the other Jump* macros.
-
- Acorn's cc will warn about 'more then 10 lines of macro argument' if
- your 'trycode' and 'catchcode' have many statements.
-
-
- Example code
- ------------
-
- void Foo( void)
- {
- if (...) Desk_JumpAuto_Throw( 2);
- }
-
- void Bar( void)
- {
- if (...) Desk_JumpAuto_Throw( 1);
- }
-
- void Somefunction( void)
- {
- Desk_JumpAuto_TryCatch(
- Foo();
- Bar(); [DO NOT 'return' or 'break' out of this block]
- ,
- printf( "Foo() or Bar() failed with longjmp( %i)...\n", Desk_jumpauto_val);
- )
- }
-
- An alternative way of doing the same thing is to use the Desk_JumpAuto_Try,
- Desk_JumpAuto_Catch and Desk_JumpAuto_EndCatch macros individually:
-
- Desk_JumpAuto_Try {
- Foo();
- Bar(); [DO NOT 'return' or 'break' out of this block]
- }
- Desk_JumpAuto_Catch {
- printf( "Foo() or Bar() failed with longjmp( %i)...\n", Desk_jumpauto_val);
- }
- Desk_JumpAuto_EndCatch
-
-
- The value passed to 'Desk_JumpAuto_Throw' is available to 'catchcode' as the
- local int 'Desk_jumpauto_val'. When Desk_JumpAuto_Error2Handler is used,
- Desk_jumpauto_val will be the Desk_error2_block* (cast into an integer).
-
- This means that, if you are using the Error2 system with
- Desk_JumpAuto_Error2Handler as the error handler, you could do:
-
- Desk_JumpAuto_TryCatch(
- ...
- ,
- printf( "Error occurred, address of Desk_error2_block is %p\n",
- (Desk_error2_block*) Desk_jumpauto_val
- )
- )
-
-
-
-
- Notes
- =====
-
-
- 'break' and 'return' with Desk_JumpAuto_*
- ------------------------------------
-
- You should *never* leave the 'try' code with a break, return, goto,
- longjmp or similar.
-
- In fact, control should only leave a try block in one of two ways: By
- reaching the end of the try block and 'falling through', or by an
- exception being thrown.
-
- The reason for this restriction is that the JumpAuto system needs to be
- told to remove a jmp_buf from its list of jmp_bufs when a 'try' block is
- exited, and this is only done by the 'JumpAuto_Catch' macro. If you
- accidently leave a 'try' block with a 'return' or similar, subsequent
- calls to Desk_JumpAuto_Throw will cause horrible crashes - the system
- will jump to a non-existant jmp_buf.
-
-
- Terminating ';'
- ---------------
-
- When using the Desk_JumpAuto_TryCatch macro, you shouldn't use a terminating
- ';'.
-
- This makes use of Desk_JumpAuto_TryCatch look rather un C-like. It would be
- possible to use a 'do ... while (0)' construct in the Desk_JumpAuto_TryCatch
- macro (as used in Desk_JumpAuto_ReturnValue, for eg), but this doesn't work
- too well if 'catchcode' is exited with a call to 'break' (using break in
- this way in 'trycode' is not allowed anyway).
-
- An alternative way of accepting a terminating ';' is to use 'if (1)
- {...} else', but this will create really bizarre and confusing errors if
- the ';' is forgoton.
-
-
- Compiler errors
- ---------------
-
- Because the Desk_JumpAuto_ macros are not part of the language, the compiler
- cannot check the syntax. If you misuse them you will generally get some
- weird errors.
-
-
- Automatic variables
- -------------------
-
- Unless they are declared as 'volatile', automatic variables that are
- modified within the try block will have an undefined value after
- the try block if a longjmp is made.
-
- For example:
-
- {
- int i = 1; // automatic variable
-
- Desk_JumpAuto_Try
- {
- i = 2; // modified in try block
- if (error) Desk_JumpAuto_Throw( 3);
- }
- Desk_JumpAuto_Catch
- {
- 'i' is undefined here
- }
- Desk_JumpAuto_EndCatch
-
- i is undefined here if a longjmp was made.
- }
-
- so do:
-
- {
- char volatile *s = NULL; // automatic variable
-
- Desk_JumpAuto_Try
- {
- s = ...; // modified in try block
- if (error) Desk_JumpAuto_Throw( 3);
- }
- Desk_JumpAuto_Catch
- {
- 's' is defined here
- }
- Desk_JumpAuto_EndCatch
-
- i is defined here even if a longjmp was made.
- }
-
-
- */
-
-
-
-
-
-
-
-
-
- #ifdef __cplusplus
- }
- #endif
-
- #endif
-