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