pjproject/pjlib/include/pj/except.h

268 lines
7.9 KiB
C

/* $Header: /pjproject-0.3/pjlib/include/pj/except.h 9 10/14/05 12:26a Bennylp $ */
#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>
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:
* - @ref page_pjlib_samples_except_c
* - @ref page_pjlib_exception_test
*
* \section pj_except_except Exception Handling
*
* This module provides exception handling syntactically similar to C++ in
* C language. The underlying mechanism use setjmp() and longjmp(), and since
* these constructs are ANSI standard, the mechanism here should be available
* on most platforms/compilers which are ANSI compliant.
*
* If ANSI libc is not available, then setjmp()/longjmp() implementation will
* be 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.
*
* 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.
* - Exceptions will always be caught by the first handle (unlike C++ where
* exception is only caught if the type matches.
*
* 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 main()
{
PJ_USE_EXCEPTION; // declare local exception stack.
PJ_TRY {
...// do something..
}
PJ_CATCH(NO_MEMORY) {
... // handle exception 1
}
PJ_CATCH(SYNTAX_ERROR) {
... // handle exception 2
}
PJ_DEFAULT {
... // handle other exceptions.
}
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.
*
* \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.
*
* \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_DEFAULT PJ_DEFAULT
* The \a PJ_DEFAULT keyword is normally followed by a block. This block will
* be executed if the exception being thrown doesn't match any of the \a
* PJ_CATCH specification. The \a PJ_DEFAULT block \b MUST be placed as the
* last block of the handlers.
*
* \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_DEFAULT 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:
* - @ref page_pjlib_samples_except_c
* - @ref page_pjlib_exception_test
*/
/**
* 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);
/** @} */
/**
* This structure (which should be invisible to user) manages the TRY handler
* stack.
*/
struct pj_exception_state_t
{
struct pj_exception_state_t *prev; /**< Previous state in the list. */
pj_jmp_buf state; /**< jmp_buf. */
};
/**
* 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_(void);
/**
* 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_DEFAULT else
/**
* End of exception specification block.
* @hideinitializer
*/
#define PJ_END pj_pop_exception_handler_(); \
} 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__)
PJ_END_DECL
#endif /* __PJ_EXCEPTION_H__ */