421 lines
13 KiB
C++
421 lines
13 KiB
C++
/*
|
|
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
|
|
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
#ifndef __PJ_EXCEPTION_H__
|
|
#define __PJ_EXCEPTION_H__
|
|
|
|
/**
|
|
* @file except.h
|
|
* @brief Exception Handling in C.
|
|
*/
|
|
|
|
#include <pj/types.h>
|
|
#include <pj/compat/setjmp.h>
|
|
#include <pj/log.h>
|
|
|
|
|
|
PJ_BEGIN_DECL
|
|
|
|
|
|
/**
|
|
* @defgroup PJ_EXCEPT Exception Handling
|
|
* @ingroup PJ_MISC
|
|
* @{
|
|
*
|
|
* \section pj_except_sample_sec Quick Example
|
|
*
|
|
* For the impatient, take a look at some examples:
|
|
* - Exception Handling sample: \src{pjlib/src/pjlib-samples/except.c}
|
|
* - Exception Handling test: \src{pjlib/src/pjlib-test/exception.c}
|
|
*
|
|
* \section pj_except_except Exception Handling
|
|
*
|
|
* This module provides exception handling syntactically similar to C++ in
|
|
* C language. In Win32 systems, it uses Windows Structured Exception
|
|
* Handling (SEH) if macro PJ_EXCEPTION_USE_WIN32_SEH is non-zero.
|
|
* Otherwise it will use setjmp() and longjmp().
|
|
*
|
|
* On some platforms where setjmp/longjmp is not available, setjmp/longjmp
|
|
* implementation is provided. See <pj/compat/setjmp.h> for compatibility.
|
|
*
|
|
* The exception handling mechanism is completely thread safe, so the exception
|
|
* thrown by one thread will not interfere with other thread.
|
|
*
|
|
* The exception handling constructs are similar to C++. The blocks will be
|
|
* constructed similar to the following sample:
|
|
*
|
|
* \verbatim
|
|
#define NO_MEMORY 1
|
|
#define SYNTAX_ERROR 2
|
|
|
|
int sample1()
|
|
{
|
|
PJ_USE_EXCEPTION; // declare local exception stack.
|
|
|
|
PJ_TRY {
|
|
...// do something..
|
|
}
|
|
PJ_CATCH(NO_MEMORY) {
|
|
... // handle exception 1
|
|
}
|
|
PJ_END;
|
|
}
|
|
|
|
int sample2()
|
|
{
|
|
PJ_USE_EXCEPTION; // declare local exception stack.
|
|
|
|
PJ_TRY {
|
|
...// do something..
|
|
}
|
|
PJ_CATCH_ANY {
|
|
if (PJ_GET_EXCEPTION() == NO_MEMORY)
|
|
...; // handle no memory situation
|
|
else if (PJ_GET_EXCEPTION() == SYNTAX_ERROR)
|
|
...; // handle syntax error
|
|
}
|
|
PJ_END;
|
|
}
|
|
\endverbatim
|
|
*
|
|
* The above sample uses hard coded exception ID. It is @b strongly
|
|
* recommended that applications request a unique exception ID instead
|
|
* of hard coded value like above.
|
|
*
|
|
* \section pj_except_reg Exception ID Allocation
|
|
*
|
|
* To ensure that exception ID (number) are used consistently and to
|
|
* prevent ID collisions in an application, it is strongly suggested that
|
|
* applications allocate an exception ID for each possible exception
|
|
* type. As a bonus of this process, the application can identify
|
|
* the name of the exception when the particular exception is thrown.
|
|
*
|
|
* Exception ID management are performed with the following APIs:
|
|
* - #pj_exception_id_alloc().
|
|
* - #pj_exception_id_free().
|
|
* - #pj_exception_id_name().
|
|
*
|
|
*
|
|
* PJLIB itself automatically allocates one exception id, i.e.
|
|
* #PJ_NO_MEMORY_EXCEPTION which is declared in <pj/pool.h>. This exception
|
|
* ID is raised by default pool policy when it fails to allocate memory.
|
|
*
|
|
* CAVEATS:
|
|
* - unlike C++ exception, the scheme here won't call destructors of local
|
|
* objects if exception is thrown. Care must be taken when a function
|
|
* hold some resorce such as pool or mutex etc.
|
|
* - You CAN NOT make nested exception in one single function without using
|
|
* a nested PJ_USE_EXCEPTION. Samples:
|
|
\verbatim
|
|
void wrong_sample()
|
|
{
|
|
PJ_USE_EXCEPTION;
|
|
|
|
PJ_TRY {
|
|
// Do stuffs
|
|
...
|
|
}
|
|
PJ_CATCH_ANY {
|
|
// Do other stuffs
|
|
....
|
|
..
|
|
|
|
// The following block is WRONG! You MUST declare
|
|
// PJ_USE_EXCEPTION once again in this block.
|
|
PJ_TRY {
|
|
..
|
|
}
|
|
PJ_CATCH_ANY {
|
|
..
|
|
}
|
|
PJ_END;
|
|
}
|
|
PJ_END;
|
|
}
|
|
|
|
\endverbatim
|
|
|
|
* - You MUST NOT exit the function inside the PJ_TRY block. The correct way
|
|
* is to return from the function after PJ_END block is executed.
|
|
* For example, the following code will yield crash not in this code,
|
|
* but rather in the subsequent execution of PJ_TRY block:
|
|
\verbatim
|
|
void wrong_sample()
|
|
{
|
|
PJ_USE_EXCEPTION;
|
|
|
|
PJ_TRY {
|
|
// do some stuffs
|
|
...
|
|
return; <======= DO NOT DO THIS!
|
|
}
|
|
PJ_CATCH_ANY {
|
|
}
|
|
PJ_END;
|
|
}
|
|
\endverbatim
|
|
|
|
* - You can not provide more than PJ_CATCH or PJ_CATCH_ANY nor use PJ_CATCH
|
|
* and PJ_CATCH_ANY for a single PJ_TRY.
|
|
* - Exceptions will always be caught by the first handler (unlike C++ where
|
|
* exception is only caught if the type matches.
|
|
|
|
* \section PJ_EX_KEYWORDS Keywords
|
|
*
|
|
* \subsection PJ_THROW PJ_THROW(expression)
|
|
* Throw an exception. The expression thrown is an integer as the result of
|
|
* the \a expression. This keyword can be specified anywhere within the
|
|
* program.
|
|
*
|
|
* \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION
|
|
* Specify this in the variable definition section of the function block
|
|
* (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception
|
|
* block.
|
|
* Actually, this is just a macro to declare local variable which is used to
|
|
* push the exception state to the exception stack.
|
|
* Note: you must specify PJ_USE_EXCEPTION as the last statement in the
|
|
* local variable declarations, since it may evaluate to nothing.
|
|
*
|
|
* \subsection PJ_TRY PJ_TRY
|
|
* The \a PJ_TRY keyword is typically followed by a block. If an exception is
|
|
* thrown in this block, then the execution will resume to the \a PJ_CATCH
|
|
* handler.
|
|
*
|
|
* \subsection PJ_CATCH PJ_CATCH(expression)
|
|
* The \a PJ_CATCH is normally followed by a block. This block will be executed
|
|
* if the exception being thrown is equal to the expression specified in the
|
|
* \a PJ_CATCH.
|
|
*
|
|
* \subsection PJ_CATCH_ANY PJ_CATCH_ANY
|
|
* The \a PJ_CATCH is normally followed by a block. This block will be executed
|
|
* if any exception was raised in the TRY block.
|
|
*
|
|
* \subsection PJ_END PJ_END
|
|
* Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks.
|
|
*
|
|
* \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void)
|
|
* Get the last exception thrown. This macro is normally called inside the
|
|
* \a PJ_CATCH or \a PJ_CATCH_ANY block, altough it can be used anywhere where
|
|
* the \a PJ_USE_EXCEPTION definition is in scope.
|
|
*
|
|
*
|
|
* \section pj_except_examples_sec Examples
|
|
*
|
|
* For some examples on how to use the exception construct, please see:
|
|
* - Exception Handling sample: \src{pjlib/src/pjlib-samples/except.c}
|
|
* - Exception Handling test: \src{pjlib/src/pjlib-test/exception.c}
|
|
*/
|
|
|
|
/**
|
|
* Allocate a unique exception id.
|
|
* Applications don't have to allocate a unique exception ID before using
|
|
* the exception construct. However, by doing so it ensures that there is
|
|
* no collisions of exception ID.
|
|
*
|
|
* As a bonus, when exception number is acquired through this function,
|
|
* the library can assign name to the exception (only if
|
|
* PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the
|
|
* exception name when it catches an exception.
|
|
*
|
|
* @param name Name to be associated with the exception ID.
|
|
* @param id Pointer to receive the ID.
|
|
*
|
|
* @return PJ_SUCCESS on success or PJ_ETOOMANY if the library
|
|
* is running out out ids.
|
|
*/
|
|
PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name,
|
|
pj_exception_id_t *id);
|
|
|
|
/**
|
|
* Free an exception id.
|
|
*
|
|
* @param id The exception ID.
|
|
*
|
|
* @return PJ_SUCCESS or the appropriate error code.
|
|
*/
|
|
PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id);
|
|
|
|
/**
|
|
* Retrieve name associated with the exception id.
|
|
*
|
|
* @param id The exception ID.
|
|
*
|
|
* @return The name associated with the specified ID.
|
|
*/
|
|
PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id);
|
|
|
|
|
|
/** @} */
|
|
|
|
#if defined(PJ_EXCEPTION_USE_WIN32_SEH) && PJ_EXCEPTION_USE_WIN32_SEH != 0
|
|
/*****************************************************************************
|
|
**
|
|
** IMPLEMENTATION OF EXCEPTION USING WINDOWS SEH
|
|
**
|
|
****************************************************************************/
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
PJ_IDECL_NO_RETURN(void)
|
|
pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN
|
|
{
|
|
RaiseException(id,1,0,NULL);
|
|
}
|
|
|
|
#define PJ_USE_EXCEPTION
|
|
#define PJ_TRY __try
|
|
#define PJ_CATCH(id) __except(GetExceptionCode()==id ? \
|
|
EXCEPTION_EXECUTE_HANDLER : \
|
|
EXCEPTION_CONTINUE_SEARCH)
|
|
#define PJ_CATCH_ANY __except(EXCEPTION_EXECUTE_HANDLER)
|
|
#define PJ_END
|
|
#define PJ_THROW(id) pj_throw_exception_(id)
|
|
#define PJ_GET_EXCEPTION() GetExceptionCode()
|
|
|
|
|
|
#elif defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
|
|
/*****************************************************************************
|
|
**
|
|
** IMPLEMENTATION OF EXCEPTION USING SYMBIAN LEAVE/TRAP FRAMEWORK
|
|
**
|
|
****************************************************************************/
|
|
|
|
/* To include this file, the source file must be compiled as
|
|
* C++ code!
|
|
*/
|
|
#ifdef __cplusplus
|
|
|
|
class TPjException
|
|
{
|
|
public:
|
|
int code_;
|
|
};
|
|
|
|
#define PJ_USE_EXCEPTION
|
|
#define PJ_TRY try
|
|
//#define PJ_CATCH(id)
|
|
#define PJ_CATCH_ANY catch (const TPjException & pj_excp_)
|
|
#define PJ_END
|
|
#define PJ_THROW(x_id) do { TPjException e; e.code_=x_id; throw e;} \
|
|
while (0)
|
|
#define PJ_GET_EXCEPTION() pj_excp_.code_
|
|
|
|
#else
|
|
|
|
#define PJ_USE_EXCEPTION
|
|
#define PJ_TRY
|
|
#define PJ_CATCH_ANY if (0)
|
|
#define PJ_END
|
|
#define PJ_THROW(x_id) do { PJ_LOG(1,("PJ_THROW"," error code = %d",x_id)); } while (0)
|
|
#define PJ_GET_EXCEPTION() 0
|
|
|
|
#endif /* __cplusplus */
|
|
|
|
#else
|
|
/*****************************************************************************
|
|
**
|
|
** IMPLEMENTATION OF EXCEPTION USING GENERIC SETJMP/LONGJMP
|
|
**
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* This structure (which should be invisible to user) manages the TRY handler
|
|
* stack.
|
|
*/
|
|
struct pj_exception_state_t
|
|
{
|
|
pj_jmp_buf state; /**< jmp_buf. */
|
|
struct pj_exception_state_t *prev; /**< Previous state in the list. */
|
|
};
|
|
|
|
/**
|
|
* Throw exception.
|
|
* @param id Exception Id.
|
|
*/
|
|
PJ_DECL_NO_RETURN(void)
|
|
pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN;
|
|
|
|
/**
|
|
* Push exception handler.
|
|
*/
|
|
PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec);
|
|
|
|
/**
|
|
* Pop exception handler.
|
|
*/
|
|
PJ_DECL(void) pj_pop_exception_handler_(struct pj_exception_state_t *rec);
|
|
|
|
/**
|
|
* Declare that the function will use exception.
|
|
* @hideinitializer
|
|
*/
|
|
#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__
|
|
|
|
/**
|
|
* Start exception specification block.
|
|
* @hideinitializer
|
|
*/
|
|
#define PJ_TRY if (1) { \
|
|
pj_push_exception_handler_(&pj_x_except__); \
|
|
pj_x_code__ = pj_setjmp(pj_x_except__.state); \
|
|
if (pj_x_code__ == 0)
|
|
/**
|
|
* Catch the specified exception Id.
|
|
* @param id The exception number to catch.
|
|
* @hideinitializer
|
|
*/
|
|
#define PJ_CATCH(id) else if (pj_x_code__ == (id))
|
|
|
|
/**
|
|
* Catch any exception number.
|
|
* @hideinitializer
|
|
*/
|
|
#define PJ_CATCH_ANY else
|
|
|
|
/**
|
|
* End of exception specification block.
|
|
* @hideinitializer
|
|
*/
|
|
#define PJ_END pj_pop_exception_handler_(&pj_x_except__); \
|
|
} else {}
|
|
|
|
/**
|
|
* Throw exception.
|
|
* @param exception_id The exception number.
|
|
* @hideinitializer
|
|
*/
|
|
#define PJ_THROW(exception_id) pj_throw_exception_(exception_id)
|
|
|
|
/**
|
|
* Get current exception.
|
|
* @return Current exception code.
|
|
* @hideinitializer
|
|
*/
|
|
#define PJ_GET_EXCEPTION() (pj_x_code__)
|
|
|
|
#endif /* PJ_EXCEPTION_USE_WIN32_SEH */
|
|
|
|
|
|
PJ_END_DECL
|
|
|
|
|
|
|
|
#endif /* __PJ_EXCEPTION_H__ */
|
|
|
|
|