blob: 3f48abdf9a76d88b72b36293b5bac17c88c19ddb [file] [log] [blame]
Benny Prijono4766ffe2005-11-01 17:56:59 +00001/* $Id$
2 *
3 */
Benny Prijonodd859a62005-11-01 16:42:51 +00004
5#ifndef __PJ_EXCEPTION_H__
6#define __PJ_EXCEPTION_H__
7
8/**
9 * @file except.h
10 * @brief Exception Handling in C.
11 */
12
13#include <pj/types.h>
14#include <pj/compat/setjmp.h>
15
16
17PJ_BEGIN_DECL
18
19
20/**
21 * @defgroup PJ_EXCEPT Exception Handling
22 * @ingroup PJ_MISC
23 * @{
24 *
25 * \section pj_except_sample_sec Quick Example
26 *
27 * For the impatient, take a look at some examples:
28 * - @ref page_pjlib_samples_except_c
29 * - @ref page_pjlib_exception_test
30 *
31 * \section pj_except_except Exception Handling
32 *
33 * This module provides exception handling syntactically similar to C++ in
34 * C language. The underlying mechanism use setjmp() and longjmp(), and since
35 * these constructs are ANSI standard, the mechanism here should be available
36 * on most platforms/compilers which are ANSI compliant.
37 *
38 * If ANSI libc is not available, then setjmp()/longjmp() implementation will
39 * be provided. See <pj/compat/setjmp.h> for compatibility.
40 *
41 * The exception handling mechanism is completely thread safe, so the exception
42 * thrown by one thread will not interfere with other thread.
43 *
44 * CAVEATS:
45 * - unlike C++ exception, the scheme here won't call destructors of local
46 * objects if exception is thrown. Care must be taken when a function
47 * hold some resorce such as pool or mutex etc.
48 * - You CAN NOT make nested exception in one single function without using
49 * a nested PJ_USE_EXCEPTION.
50 * - Exceptions will always be caught by the first handle (unlike C++ where
51 * exception is only caught if the type matches.
52 *
53 * The exception handling constructs are similar to C++. The blocks will be
54 * constructed similar to the following sample:
55 *
56 * \verbatim
57 #define NO_MEMORY 1
58 #define SYNTAX_ERROR 2
59
60 int main()
61 {
62 PJ_USE_EXCEPTION; // declare local exception stack.
63
64 PJ_TRY {
65 ...// do something..
66 }
67 PJ_CATCH(NO_MEMORY) {
68 ... // handle exception 1
69 }
70 PJ_CATCH(SYNTAX_ERROR) {
71 ... // handle exception 2
72 }
73 PJ_DEFAULT {
74 ... // handle other exceptions.
75 }
76 PJ_END;
77 }
78 \endverbatim
79 *
80 * The above sample uses hard coded exception ID. It is @b strongly
81 * recommended that applications request a unique exception ID instead
82 * of hard coded value like above.
83 *
84 * \section pj_except_reg Exception ID Allocation
85 *
86 * To ensure that exception ID (number) are used consistently and to
87 * prevent ID collisions in an application, it is strongly suggested that
88 * applications allocate an exception ID for each possible exception
89 * type. As a bonus of this process, the application can identify
90 * the name of the exception when the particular exception is thrown.
91 *
92 * Exception ID management are performed with the following APIs:
93 * - #pj_exception_id_alloc().
94 * - #pj_exception_id_free().
95 * - #pj_exception_id_name().
96 *
97 *
98 * PJLIB itself automatically allocates one exception id, i.e.
99 * #PJ_NO_MEMORY_EXCEPTION which is declared in <pj/pool.h>. This exception
100 * ID is raised by default pool policy when it fails to allocate memory.
101 *
102 * \section PJ_EX_KEYWORDS Keywords
103 *
104 * \subsection PJ_THROW PJ_THROW(expression)
105 * Throw an exception. The expression thrown is an integer as the result of
106 * the \a expression. This keyword can be specified anywhere within the
107 * program.
108 *
109 * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION
110 * Specify this in the variable definition section of the function block
111 * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception
112 * block.
113 * Actually, this is just a macro to declare local variable which is used to
114 * push the exception state to the exception stack.
115 *
116 * \subsection PJ_TRY PJ_TRY
117 * The \a PJ_TRY keyword is typically followed by a block. If an exception is
118 * thrown in this block, then the execution will resume to the \a PJ_CATCH
119 * handler.
120 *
121 * \subsection PJ_CATCH PJ_CATCH(expression)
122 * The \a PJ_CATCH is normally followed by a block. This block will be executed
123 * if the exception being thrown is equal to the expression specified in the
124 * \a PJ_CATCH.
125 *
126 * \subsection PJ_DEFAULT PJ_DEFAULT
127 * The \a PJ_DEFAULT keyword is normally followed by a block. This block will
128 * be executed if the exception being thrown doesn't match any of the \a
129 * PJ_CATCH specification. The \a PJ_DEFAULT block \b MUST be placed as the
130 * last block of the handlers.
131 *
132 * \subsection PJ_END PJ_END
133 * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks.
134 *
135 * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void)
136 * Get the last exception thrown. This macro is normally called inside the
137 * \a PJ_CATCH or \a PJ_DEFAULT block, altough it can be used anywhere where
138 * the \a PJ_USE_EXCEPTION definition is in scope.
139 *
140 *
141 * \section pj_except_examples_sec Examples
142 *
143 * For some examples on how to use the exception construct, please see:
144 * - @ref page_pjlib_samples_except_c
145 * - @ref page_pjlib_exception_test
146 */
147
148/**
149 * Allocate a unique exception id.
150 * Applications don't have to allocate a unique exception ID before using
151 * the exception construct. However, by doing so it ensures that there is
152 * no collisions of exception ID.
153 *
154 * As a bonus, when exception number is acquired through this function,
155 * the library can assign name to the exception (only if
156 * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the
157 * exception name when it catches an exception.
158 *
159 * @param name Name to be associated with the exception ID.
160 * @param id Pointer to receive the ID.
161 *
162 * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library
163 * is running out out ids.
164 */
165PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name,
166 pj_exception_id_t *id);
167
168/**
169 * Free an exception id.
170 *
171 * @param id The exception ID.
172 *
173 * @return PJ_SUCCESS or the appropriate error code.
174 */
175PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id);
176
177/**
178 * Retrieve name associated with the exception id.
179 *
180 * @param id The exception ID.
181 *
182 * @return The name associated with the specified ID.
183 */
184PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id);
185
186/** @} */
187
188/**
189 * This structure (which should be invisible to user) manages the TRY handler
190 * stack.
191 */
192struct pj_exception_state_t
193{
194 struct pj_exception_state_t *prev; /**< Previous state in the list. */
195 pj_jmp_buf state; /**< jmp_buf. */
196};
197
198/**
199 * Throw exception.
200 * @param id Exception Id.
201 */
202PJ_DECL_NO_RETURN(void)
203pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN;
204
205/**
206 * Push exception handler.
207 */
208PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec);
209
210/**
211 * Pop exception handler.
212 */
213PJ_DECL(void) pj_pop_exception_handler_(void);
214
215/**
216 * Declare that the function will use exception.
217 * @hideinitializer
218 */
219#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__
220
221/**
222 * Start exception specification block.
223 * @hideinitializer
224 */
225#define PJ_TRY if (1) { \
226 pj_push_exception_handler_(&pj_x_except__); \
227 pj_x_code__ = pj_setjmp(pj_x_except__.state); \
228 if (pj_x_code__ == 0)
229/**
230 * Catch the specified exception Id.
231 * @param id The exception number to catch.
232 * @hideinitializer
233 */
234#define PJ_CATCH(id) else if (pj_x_code__ == (id))
235
236/**
237 * Catch any exception number.
238 * @hideinitializer
239 */
240#define PJ_DEFAULT else
241
242/**
243 * End of exception specification block.
244 * @hideinitializer
245 */
246#define PJ_END pj_pop_exception_handler_(); \
247 } else {}
248
249/**
250 * Throw exception.
251 * @param exception_id The exception number.
252 * @hideinitializer
253 */
254#define PJ_THROW(exception_id) pj_throw_exception_(exception_id)
255
256/**
257 * Get current exception.
258 * @return Current exception code.
259 * @hideinitializer
260 */
261#define PJ_GET_EXCEPTION() (pj_x_code__)
262
263PJ_END_DECL
264
265
266
267#endif /* __PJ_EXCEPTION_H__ */
268
269