/*
	File:		Except.h

	Contains:	Exception handling macros

	Owned by:	Jens Alfke
	Written by:	Jens Alfke, Richard Rodseth, Steve Smith
				Some macros stolen from ASLM

	Copyright:	c 1993-94 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		 <1>	10/24/94	jpa		first checked in
		<11>	  9/9/94	jpa		Added SOMCHKEXCEPT. Fixed typo in
									SOM_ENDTRY. Moved debugging stuff to new
									ODDebug.h. [1185656]
		<10>	  8/2/94	jpa		Added BreakOnThrow.
		 <9>	 7/26/94	jpa		Added SetOutputMode and GetOutputMode.
		 <8>	 7/15/94	jpa		Don't put filenames in _AssertionFailed
									calls in MPW builds (avoid full pathnames.)
		 <7>	 7/11/94	jpa		Added ODInitExceptions. Made most of API
									extern "C".
		 <6>	  7/1/94	jpa		Added ODSetSOMException.
		 <5>	  6/6/94	jpa		Volatile->ODVolatile. XMP->OD. ENDTRY_SOM
									-> SOM_ENDTRY. Added SetEnvException.
		 <4>	 5/26/94	jpa		Added more SOM stuff.
		 <3>	 5/13/94	RR		XMP->OD.
		<19>	 4/12/94	eeh		bug #1146751: remove WriteToFile
		<18>	  2/9/94	NP		Tiger Team cleanup.
		<17>	  2/7/94	JA		Capitalized THROW functions.
		<16>	  2/7/94	JA		Tiger Team Makeover!
		<15>	 1/31/94	JA		Made assertions no-ops in non debug builds.
									Added logging functions.
		<14>	 12/3/93	T€		Rename ODError.h to ErrorDef.h
		<13>	 9/22/93	JA		Made new assertion macros.
		<12>	  9/8/93	NP		Put back LibraryManager.h. GRRRRRRR.
		<11>	  9/8/93	SS		Removed stream class includes; moved stdio
									includes to .cp
		<10>	 8/25/93	SS		Moved functions to Except.cp - added
									constants for new logging functionality
		 <9>	  7/12/93	RCR		Added XMPVolatile.
		 <8>	 6/18/93	RCR		Added ThrowIfNULL and Throw.
		 <7>	 6/15/93	VL		Made ThrowIfError an inline.
		 <3>	 6/11/93	RCR		Added ASSERT Macro
		 <2>	 6/10/93	RCR		Change to new SLM Exception macros
		 <1>	 6/10/93	RCR		first checked in

	To Do:
*/


/*
	THEORY OF OPERATION
	
	This is an exception handling package loosely based on the one provided
	with the Apple Shared Library Manager. It is 'throw' and 'catch' based
	like many others, including the forthcoming ANSI standard C++ exception
	system (which it should be source compatible with if a few macros are
	redefined.)

	Example Usage:
	
		ODFoo* obj = kODNULL;
		ODVolatile(obj);			// if it's modified in TRY, and used in CATCH
								// it must be kept out of registers
		
		TRY
		
			obj = new ODFoo;
			FailNULL(obj, kODErrOutOfMemory);	// Until global new throws exception
			obj->Initialize();	// Initialization that can fail
		
		CATCH(kODErrOutOfMemory)
		
			DeleteObject(&obj);		// i.e. if (obj) { delete obj; obj == NULL }
		
		CATCH_ALL
		
			// Do some stuff
			RERAISE; // or: Fail(error);
					
		ENDTRY
	
	Some RULES:
	1) Never propogate a failure outside of a constructor or destructor.
	   If your constructor or destructor can call something which fails, it
	   _must_ CATCH the failure and not re-propogate it.
	2) Never create an object inside of a "TRY" block which you cannot 
	   destroy (especially an auto object).
	3) if you are going to just RERAISE the exception, or Fail 
	   with a different error, you must manually call the destructors 
	   of any  auto objects that are still in scope!
	4) Any variables that are changed inside the "TRY", and which are tested
	   inside a CATCH, CATCH_ALL, or FINALLY must be declared "volatile" 
	   (Use the Volatile macro below until C++ and volatile work!)
	5) Never call Fail while an auto variable is in scope - it's 
	   destructor will not be called unless you call it manually.
	   	   
	Limitations: 
		One Exception type (a long integer value)
		Catch block must rethrow errors it doesn't handle
		No automatic calling of destructors
		Use of VOLATILE (See below)
		Maybe some others
*/

#ifndef _EXCEPT_
#define _EXCEPT_

#ifndef _ODTYPES_
#include "ODTypes.h"
#endif

#ifndef _ERRORDEF_
#include "ErrorDef.xh"	// Clients probably need the error codes as well
#endif

#ifndef __SETJMP__
#include <setjmp.h>
#endif

#ifdef PLATFORM_MACINTOSH
#ifdef __LIBRARYMANAGER__
#error "Please don't include both Except.h and LibraryManager.h"
// Actually you can do it if you include Except.h first but undefine
// the TRY, CATCH, CATCH_ALL, RERAISE, and ENDTRY macros before
// including LibraryManager.h. But only do this if you REALLY know
// what you're doing! (See UseRsrcM.cpp for an example.)
#endif
#endif

#ifdef __cplusplus
extern "C" {
#endif


//=====================================================================================
// Exception Handling Macros
//=====================================================================================

#ifdef PLATFORM_MACINTOSH
#define ErrorCode() 				(_except.fError)
#define ErrorMessage()				(_except.fMessage)

#define TRY 												\
	{														\
		ODExceptionFrame _except;							\
		ODVolatile(_except);								\
		if (setjmp(_except.fBuffer) == 0)					\
		{
		
#define CATCH(e)											\
		}													\
		else if (_except.Match(e))							\
		{ 
		
		
#define CATCH_ALL											\
		}													\
		else												\
		{ 
		
		
#define RERAISE												\
		_except.Reraise()
		
				
#define ENDTRY												\
		}													\
		_except.Pop()		;								\
	}
#endif //PLATFORM_MACINTOSH

#ifdef PLATFORM_WINDOWS
#ifdef DEFLASTERROR
ODError lastError;
#else
extern ODError lastError;
#endif

#define TRY \
	{ \
		try \
		{
					// The handler in this __except block processes ONLY 
					// the error passed in as the CATCH parameter "declaration".
#define CATCH(declaration) \
		} \
		catch(declaration) \
		{

#define CATCH_ALL \
		} \
		catch (...) \
		{ 

#define RERAISE \
		throw lastError;

//bgw - for MESSAGIN
#define ErrorCode() (lastError)

#define ENDTRY \
			} \
		}

//bgn these are defined to do nothing until I can figure out what to do.
#define SOM_CATCH	if(0)
#define FN_CATCH	if(0)
 
void THROW_IF_ERROR(ODError error, char* msg = kODNULL);
void THROW(ODError error, char* msg = kODNULL);
void THROW_IF_NULL(void* value, char* msg = kODNULL);
//bgnvoid THROW_IF_NULL(unsigned int value, char* msg = kODNULL);

#endif

#ifdef PLATFORM_MACINTOSH
//=====================================================================================
// Raising Exceptions
//=====================================================================================

void THROW(ODError error);
void THROW_IF_ERROR(ODError error);
void THROW_IF_NULL(void* value);

// Optional message parameters:
void THROW_IF_ERROR_M(ODError error, const char* msg);
void THROW_M(ODError error, const char* msg);
void THROW_IF_NULL_M(void* value, const char* msg);

// Call BreakOnThrow(TRUE) to break into the debugger whenever THROW is called.
// (The call returns the previous value of the setting.)
ODBoolean BreakOnThrow( ODBoolean brk );
#endif


//=====================================================================================
// ODVolatile
//=====================================================================================

// Any variable or parameter that is modified in a TRY block and used in the
// CATCH block must be declared as volatile or it may have an incorrect value
// in the CATCH block.
// Since not all compilers support the 'volatile' keyword, use this instead;

#define ODVolatile(x)	((void) &x)

#ifdef PLATFORM_MACINTOSH
//=====================================================================================
// Fn_Catch (Fuction-wide exception handlers)
//=====================================================================================

/*	FN_CATCH is a macro for use in functions that want to use exceptions (or
			 call functions that may throw exceptions) but which cannot
			 propagate an exception out of the function. Such functions might
			 be Toolbox callbacks or SOM methods. Example:
				OSErr foo( ) {
					FN_CATCH
						return ErrorCode();
					
					fn_that_might_throw();
					return noErr;
				}
*/

#define FN_CATCH											\
		ODFnExceptionFrame _except;							\
		ODVolatile(_except);								\
		if( setjmp(_except.fBuffer) != 0 )					\
			for( ; true; _except.SpazDidntReturn() )

#endif

//=====================================================================================
// SOM Exception Utilities
//=====================================================================================

// ODSetSOMException stores an OD error code in the environment.
// ODGetSOMException returns the OD error code (if any) from an environment.

void	ODSetSOMException( Environment*, ODError, const char *message =kODNULL );
ODError	ODGetSOMException( Environment *ev );

#ifdef PLATFORM_MACINTOSH
// SetEnvException sets the SOM environment from an exception.
//					Use it in CATCH handlers (or just use SOM_ENDTRY.)
//#define SetEnvException		_except.SetSOMException		//OBSOLETE

// Use SOM_ENDTRY instead of a CATCH_ALL ... ENDTRY block.

#define SOM_ENDTRY											\
		CATCH_ALL											\
			_except.SetSOMException(ev);					\
		ENDTRY
		
// SOM_CATCH is like FN_CATCH (above) but automatically sets the SOM environment.
//			 Use it as a single line at the top of a function with a return
//			 statement after it.

#define SOM_CATCH											\
		ODFnExceptionFrame _except;							\
		ODVolatile(_except);								\
		if( setjmp(_except.fBuffer) != 0 )					\
			for( _except.SetSOMException(ev); true; _except.SpazDidntReturn() )

// CHECK_ENV throws an exception if the environment indicates an error.

void	CHECK_ENV( Environment* );

// SOMCHKEXCEPT is a macro that is called in a .xh file if the ev variable
// indicates an exception is set.
#define SOMCHKEXCEPT {CHECK_ENV(ev);}

#endif


//=====================================================================================
// Exception Constants
//=====================================================================================

#define exceptionMask	0x0000FFFF
#define methodMask		0x00FF0000
#define classMask		0xFF000000

#define kExceptionList	100
#define kClassList		101


#ifdef __cplusplus
}
#endif


#ifdef PLATFORM_MACINTOSH
// C++ style overloaded alternatives to THROW_M etc.

inline void THROW_IF_ERROR(ODError error, const char* msg)
							{THROW_IF_ERROR_M(error,msg);}
inline void THROW(ODError error, const char* msg)
							{THROW_M(error,msg);}
inline void THROW_IF_NULL(void* value, const char* msg)
							{THROW_IF_NULL_M(value,msg);}


//=====================================================================================
// The Exception Handler (ODExceptionFrame class)
//=====================================================================================

struct ODExceptionFrame
{
	jmp_buf				fBuffer;
	ODExceptionFrame*	fPrev;
	const char*			fMessage;
	ODError				fError;
	
						ODExceptionFrame( );
	long				Pop( );
	BOOL				Match( ODError );
	void				Reraise( );
//bgn	void				SetSOMException( Environment* );
};

struct ODFnExceptionFrame :public ODExceptionFrame {
			~ODFnExceptionFrame( );
	void	SpazDidntReturn( );
};
#endif

#endif // _EXCEPT_
