Code Search for Developers
 
 
  

NSXException.h from redshed at Krugle


Show NSXException.h syntax highlighted

/****************************************************************************************
	NSXException.h $Revision: 47 $
	
	Copyright (c) 2002 Red Shed Software. All rights reserved.
	by Jonathan 'Wolf' Rentzsch (jon * redshed * net)
	
	************************************************************************************/

/************************************************************************************//**
	@mainpage	NSXException
	@author		Jonathan 'Wolf' Rentzsch (jon * redshed * net)
	
	This package, coded in ObjC++, allows:
	
	@li Throwing C++ exceptions across C/ObjC boundaries.
	@li Raising ObjC exceptions across C++ boundaries.
	@li Lossless morphing of C++ exceptions to ObjC exceptions and back.
	@li Lossless morphing of ObjC exceptions to C++ exceptions and back.
	@li Use stack-based C++ objects (like auto_ptr<>) in face of NSException-raising
		code.
	
	Use it by #include'ing NSXException.h from your .mm (ObjC++) file.
	
	@todo	Stop using NSData to wrap std::exception pointers and write an general ObjC
			C++ pointer wrapper which deletes its wrapped pointer upon dealloc. (Right
			now the C++ exception pointer is leaked.)

	************************************************************************************/

#ifndef		_NSXException_
#define		_NSXException_

#include	<Cocoa/Cocoa.h>
#include	<exception>
#include	<typeinfo>

/****************************************************************************************
*	
*	Classes
*	
****************************************************************************************/
#pragma mark	(Classes)

/************************************************************************************//**
	Main class & C++ wrapper object for ObjC NSException objects.

	************************************************************************************/

class	NSXException : public std::exception {
public:
	/** Constructs the object with the ObjC exception to wrap. */
	NSXException(
		NSException *nsexception )
	:	nsexception_( nsexception ){}
	
	/** Returns the enclosed ObjC exception. */
		NSException*
	nsexception() const
	{	return nsexception_; }
	
	/** Raises the enclosed ObjC exception. */
		void
	raise() const {
		[nsexception_ raise];
	}
	
	/** Just returns the class name, like std::exception::what() does. */
		virtual
		const char*
	what() const throw() {
		return "NSXException";
	}
	
	/************************************************************************************//**
		Given a C++ std::exception object, will return an ObjC NSException object
		representing the exception. Is smart in that it looks at the exception and will
		simply "unwrap" it if it's a C++-wrapper ObjC NSException already. I call this
		"morphing", and it's lossless.
		
		@param	cppException	Required pointer to C++ exception.
		@result	the ObjC NSException object
	
		************************************************************************************/
		
		static
		NSException*
	NSExceptionForException(
		const	std::exception	*cppException )
	{
		const NSXException *nsxexception = dynamic_cast<const NSXException*>( cppException );
		if( nsxexception ) {
			//	It's a wrapped NSException -- unwrap it.
			return nsxexception->nsexception();
		} else {
			//	It's a C++ exception -- wrap it in an NSException.
			NSData *cppExceptionWrapper = [NSData dataWithBytes:&cppException length:sizeof(cppException)];
			NSDictionary *userInfo = [NSDictionary
										dictionaryWithObject: cppExceptionWrapper
										forKey:@"std::exception"];
			NSString *reason = [NSString stringWithFormat:@"%s: %s", typeid(cppException).name(), cppException->what()];
			return [NSException
					exceptionWithName:@"NSException-wrapped std::exception"
					reason:reason
					userInfo:userInfo];
		}
	}

	/************************************************************************************//**
		Morphs a C++ exception into an ObjC exception and raises it.
		
		@param	cppException	Required pointer to C++ exception.
		
		************************************************************************************/
	
		static
		void
	Raise(
		const	std::exception	*cppException )
	{
		[NSXException::NSExceptionForException( cppException ) raise];
	}
	
	/************************************************************************************//**
		Morphs an ObjC exception into a C++ exception and throws it.
		
		@param	objCException	Required pointer to ObjC NSException exception.
		
		************************************************************************************/
		
		static
		void
	Throw(
		NSException	*objCException )
	{
		NSDictionary *userInfo = [objCException userInfo];
		NSData *cppException = [userInfo valueForKey:@"std::exception"];
		if( cppException ) {
			std::exception *exception;
			[cppException getBytes:&exception];
			throw exception;
		} else {
			throw new NSXException( objCException );
		}
	}
private:
	NSException	*nsexception_;
};

/************************************************************************************//**
	Scope-aware C++ wrapper for NSException's _NSHandler2. _NSHandlers are installed via
	_NSAddHandler2() and removed via _NSRemoveHandler2 -- they maintain the setjmp
	context buffer and allow access to it later on down the call chain. The last
	NSHandler installed is removed from the thread and longjmp()'d to when a NSException
	is raised. If an exception is not raised, then the _NSHandler2 must be manually
	removed.
	
	This class wraps _NSHandler2 into a scope-aware C++ class, allowing automated
	installation and removal (and avoiding the need for the NS_[VOID|VALUE]RETURN hack).
	
	************************************************************************************/

class	AutoNSExceptionHandler : public _NSHandler2 {
public:
	/**	Constructor installs a local _NSHandler2 */
	AutoNSExceptionHandler()
	:	installed_( true ) {
		_NSAddHandler2( this );
	}
	
	/**	Destructor removes the local _NSHandle2 if it wasn't already removed by an
		exception being raised. */
	~AutoNSExceptionHandler() {
		if( installed_ ) {
			_NSRemoveHandler2( this );
			installed_ = false;
		}
	}
	
	/** Returns the ObjC NSException object that was raised. Always use this to retrieve
		the exception object, as it notes an exception was raised and remembers not to
		try to remove the local handler it previously installed. */
	NSException* localException() {
		installed_ = false;
		return _NSExceptionObjectFromHandler2( this );
	}
private:
	bool	installed_;
};

/************************************************************************************//**
	Usually raising an ObjC NSException invokes a longjmp out of the current function.
	Unfortunately, longjmp is rather primative and knows nothing about C++, so your
	stack-based objects' destructors will never be called.
	
	This class is a hack that gets around that. Declare a stack-based instance of it at
	the very top of your function/method. Upon destruction, hopefully after all your
	other stack-based objects have been destructed, it will raise an exception if it has
	been "armed". Use it like this:
	
	@code
	void foo() {
		NSXRaisingDestructor raisingDestructor;
		auto_ptr<BigStruct> bar = new BigStruct();
		try {
			ThrowNSX( [obj foo] );
		} catch( const std::exception *x ) {
			raisingDestructor.arm( x );
		}
		// bar's destructor will correctly be called here for you
		// raisingDestructor's destructor will re-raise the ObjC exeception here for you.
	}
	@endcode
	
	************************************************************************************/

class	NSXRaisingDestructor	{
public:
	/** Default constructor. */
	NSXRaisingDestructor()
	:	nsexception_( nil ){}
	
	/** Destructor raises an NSException if it's been "armed". */
	~NSXRaisingDestructor() {
		if( nsexception_ != nil )
			[nsexception_ raise];
	}
	
	/** Stores away a pointer to the ObjC NSException for raising in the destructor */
	void arm( NSException *exception ) {
		nsexception_ = exception;
	}
	
	/** Morphs the given C++ exception to a ObjC NSException for later raising in the
		destructor. */
	void arm( const std::exception *exception ) {
		nsexception_ = NSXException::NSExceptionForException( exception );
	}
private:
	NSException	*nsexception_;
};

/****************************************************************************************
*	
*	Multiline Exception Morphing
*	
****************************************************************************************/
#pragma mark	-
#pragma mark	(Multiline Exception Morphing Macros)

/************************************************************************************//**
	Better version of NSX_DURING/NSX_HANDLER/NSX_ENDHANDLER if you don't need a custom
	handler (which you usually don't if you're using stack-based objects). Automatically
	handles raised ObjC exceptions, morphs them into C++ exceptions and throws them. Use
	it like this:
	
	@code
	void foo() {
		try {
			// This will always be destructed, even if an exception is raised below.
			auto_ptr<BigStruct> bsOne = new BigStruct();
			
			NSX_THROW
				[obj1 msg1];
				[obj2 msg2];
				
				safeCFunction();
				bsOne->possiblyThrowingMethod();
				
				[obj3 msg3];
			NSX_ENDTHROW
			
			// The following is never constructed if exception is raised above.
			auto_ptr<BigStruct> bsTwo = new BigStruct();
		} catch( const std::exception *x ) {
			NSXException::Raise( x );
		}
	}
	@endcode
	
	@see NSX_ENDTHROW
	
	************************************************************************************/
	
#define	NSX_THROW									\
	{												\
		AutoNSExceptionHandler	handler_;			\
		if( !_NSSETJMP( handler_._state, 0 ) ) {

/************************************************************************************//**
	Counterpart to NSX_THROW.
	
	@see NSX_THROW
	
	************************************************************************************/					
		
#define	NSX_ENDTHROW											\
		} else {												\
			NSXException::Throw( handler_.localException() );	\
		}														\
	}

/************************************************************************************//**
	NSX_THROW's evil twin. Automatically catches thrown C++ exception pointers, morphs
	them into ObjC exception objects and raises them via NSXRaisingDestructor. Use it
	like this:
	
	@code
	- (void)foo() {
		[obj1 msg1];
		[obj2 msg2];
		
		NSX_RAISE
			// This will always be destructed, even if an exception is raised below.
			auto_ptr<BigStruct> bsOne = new BigStruct();
			bsOne->possiblyThrowingMethod();
		NSX_ENDRAISE
		
		[obj3 msg3];
	}
	@endcode
	
	@see NSX_ENDTHROW
	
	************************************************************************************/
	
#define	NSX_RAISE									\
	{												\
		NSXRaisingDestructor raisingDestructor;		\
		try {

/************************************************************************************//**
	Counterpart to NSX_RAISE.
	
	@see NSX_RAISE
	
	************************************************************************************/					
		
#define	NSX_ENDRAISE							\
		} catch( const std::exception *x ) {	\
			raisingDestructor.arm( x );			\
		}										\
	}

/****************************************************************************************
*	
*	Single-line Exception Morphing Macros
*	
****************************************************************************************/
#pragma mark	-
#pragma mark	(Single-line Exception Morphing Macros)

/************************************************************************************//**
	Wraps a line of ObjC code in a handler (which handles raised ObjC exceptions, morphs
	them into C++ exceptions and throws them). While easy to use, it bloats produced
	code, so use it sparingly. (Use NSX_THROW/NSX_ENDTHROW for long ObjC code runs.)
	
	Use it like this:
	
	@code
	void foo() {
		try {
			// This will always be destructed, even if an exception is raised below.
			auto_ptr<BigStruct> bsOne = new BigStruct();
			
			ThrowNSX( int result = [obj1 msg1] );
			bsOne->possiblyThrowingMethod( result );
		} catch( const std::exception *x ) {
			NSXException::Raise( x );
		}
	}
	@endcode
	
	@see NSX_THROW
	
	************************************************************************************/

#define	ThrowNSX( CODE )										\
	{															\
		AutoNSExceptionHandler	handler_;						\
		if( !_NSSETJMP( handler_._state, 0 ) ) {				\
			CODE;												\
		} else {												\
			NSXException::Throw( handler_.localException() );	\
		}														\
	}

/************************************************************************************//**
	Wraps a line of C++ code in a try/catch block (which catches thrown C++ exceptions,
	morphs them into ObjC exceptions and raises them via NSXRaisingDestructor). While
	easy to use, it bloats produced code, so use it sparingly. (Use
	NSX_RAISE/NSX_ENDRAISE for long C++ code runs.)
	
	Use it like this:
	
	@code
	- (void)foo() {
		[obj1 msg1];
		[obj2 msg2];
		
		RaiseNSX( int result = cppObject->possiblyThrowingMethod() );
		
		[obj3 msg3];
	}
	@endcode
	
	@see NSX_RAISE
	
	************************************************************************************/

#define	RaiseNSX( CODE )						\
	{											\
		NSXRaisingDestructor raisingDestructor;	\
		try {									\
			CODE;								\
		} catch( const std::exception *x ) {	\
			raisingDestructor.arm( x );			\
		}										\
	}

/****************************************************************************************
*	
*	Scope-Aware Version of NS_DURING. Typically you should use NSX_THROW instead.
*	
****************************************************************************************/
#pragma mark	-
#pragma mark	(Scope-Aware Version of NS_DURING)

/************************************************************************************//**
	Drop-in replacement for NS_DURING, except uses AutoNSExceptionHandler and thus
	doesn't require the NS_[VOID|VALUE]RETURN hack.
	
	@see NSX_HANDLER NSX_ENDHANDLER
	
	************************************************************************************/	

#define	NSX_DURING														\
	{																	\
		AutoNSExceptionHandler	handler_;								\
		if( !_NSSETJMP( handler_._state, 0 ) ) {						

/************************************************************************************//**
	Denotes beginning of handler block started by NSX_DURING.
	
	@see NSX_DURING NSX_ENDHANDLER
	
	************************************************************************************/						

#define	NSX_HANDLER														\
		} else {														\
			NSException	*localException = handler_.localException();	

/************************************************************************************//**
	Denotes ending of handler block started by NSX_DURING.
	
	@see NSX_DURING NSX_HANDLER
	
	************************************************************************************/	

#define	NSX_ENDHANDLER													\
			localException = localException;							\
		}																\
	}	
		
#endif	//	_NSXException_



See more files for this project here

redshed

Code for Mac+WebObjects.

Project homepage: http://sourceforge.net/projects/redshed
Programming language(s): C,Java,Objective C
License: other

  NSXException.h
  TestNSXException.mm