blob: df9bb4613a8ed31fb95cd91305ef02ea699dd6a5 [file] [log] [blame]
Benny Prijono9033e312005-11-21 02:08:39 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono9033e312005-11-21 02:08:39 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#ifndef __PJ_EXCEPTION_H__
21#define __PJ_EXCEPTION_H__
22
23/**
24 * @file except.h
25 * @brief Exception Handling in C.
26 */
27
28#include <pj/types.h>
29#include <pj/compat/setjmp.h>
Benny Prijonoa0c8b5c2007-05-12 15:03:23 +000030#include <pj/log.h>
Benny Prijono9033e312005-11-21 02:08:39 +000031
32
33PJ_BEGIN_DECL
34
35
36/**
37 * @defgroup PJ_EXCEPT Exception Handling
38 * @ingroup PJ_MISC
39 * @{
40 *
41 * \section pj_except_sample_sec Quick Example
42 *
43 * For the impatient, take a look at some examples:
44 * - @ref page_pjlib_samples_except_c
45 * - @ref page_pjlib_exception_test
46 *
47 * \section pj_except_except Exception Handling
48 *
49 * This module provides exception handling syntactically similar to C++ in
Benny Prijonod0d44f52005-11-21 16:57:02 +000050 * C language. In Win32 systems, it uses Windows Structured Exception
51 * Handling (SEH) if macro PJ_EXCEPTION_USE_WIN32_SEH is non-zero.
52 * Otherwise it will use setjmp() and longjmp().
Benny Prijono9033e312005-11-21 02:08:39 +000053 *
Benny Prijonod0d44f52005-11-21 16:57:02 +000054 * On some platforms where setjmp/longjmp is not available, setjmp/longjmp
55 * implementation is provided. See <pj/compat/setjmp.h> for compatibility.
Benny Prijono9033e312005-11-21 02:08:39 +000056 *
57 * The exception handling mechanism is completely thread safe, so the exception
58 * thrown by one thread will not interfere with other thread.
59 *
Benny Prijono9033e312005-11-21 02:08:39 +000060 * The exception handling constructs are similar to C++. The blocks will be
61 * constructed similar to the following sample:
62 *
63 * \verbatim
64 #define NO_MEMORY 1
65 #define SYNTAX_ERROR 2
66
Benny Prijonod0d44f52005-11-21 16:57:02 +000067 int sample1()
Benny Prijono9033e312005-11-21 02:08:39 +000068 {
69 PJ_USE_EXCEPTION; // declare local exception stack.
70
71 PJ_TRY {
72 ...// do something..
73 }
74 PJ_CATCH(NO_MEMORY) {
75 ... // handle exception 1
76 }
Benny Prijonod0d44f52005-11-21 16:57:02 +000077 PJ_END;
78 }
79
80 int sample2()
81 {
82 PJ_USE_EXCEPTION; // declare local exception stack.
83
84 PJ_TRY {
85 ...// do something..
Benny Prijono9033e312005-11-21 02:08:39 +000086 }
Benny Prijonod0d44f52005-11-21 16:57:02 +000087 PJ_CATCH_ANY {
88 if (PJ_GET_EXCEPTION() == NO_MEMORY)
89 ...; // handle no memory situation
90 else if (PJ_GET_EXCEPTION() == SYNTAX_ERROR)
91 ...; // handle syntax error
Benny Prijono9033e312005-11-21 02:08:39 +000092 }
93 PJ_END;
94 }
95 \endverbatim
96 *
97 * The above sample uses hard coded exception ID. It is @b strongly
98 * recommended that applications request a unique exception ID instead
99 * of hard coded value like above.
100 *
101 * \section pj_except_reg Exception ID Allocation
102 *
103 * To ensure that exception ID (number) are used consistently and to
104 * prevent ID collisions in an application, it is strongly suggested that
105 * applications allocate an exception ID for each possible exception
106 * type. As a bonus of this process, the application can identify
107 * the name of the exception when the particular exception is thrown.
108 *
109 * Exception ID management are performed with the following APIs:
110 * - #pj_exception_id_alloc().
111 * - #pj_exception_id_free().
112 * - #pj_exception_id_name().
113 *
114 *
115 * PJLIB itself automatically allocates one exception id, i.e.
116 * #PJ_NO_MEMORY_EXCEPTION which is declared in <pj/pool.h>. This exception
117 * ID is raised by default pool policy when it fails to allocate memory.
118 *
Benny Prijonof561b7e2009-08-14 10:41:00 +0000119 * CAVEATS:
120 * - unlike C++ exception, the scheme here won't call destructors of local
121 * objects if exception is thrown. Care must be taken when a function
122 * hold some resorce such as pool or mutex etc.
123 * - You CAN NOT make nested exception in one single function without using
124 * a nested PJ_USE_EXCEPTION. Samples:
125 \verbatim
126 void wrong_sample()
127 {
128 PJ_USE_EXCEPTION;
129
130 PJ_TRY {
131 // Do stuffs
132 ...
133 }
134 PJ_CATCH_ANY {
135 // Do other stuffs
136 ....
137 ..
138
139 // The following block is WRONG! You MUST declare
140 // PJ_USE_EXCEPTION once again in this block.
141 PJ_TRY {
142 ..
143 }
144 PJ_CATCH_ANY {
145 ..
146 }
147 PJ_END;
148 }
149 PJ_END;
150 }
151
152 \endverbatim
153
154 * - You MUST NOT exit the function inside the PJ_TRY block. The correct way
155 * is to return from the function after PJ_END block is executed.
156 * For example, the following code will yield crash not in this code,
157 * but rather in the subsequent execution of PJ_TRY block:
158 \verbatim
159 void wrong_sample()
160 {
161 PJ_USE_EXCEPTION;
162
163 PJ_TRY {
164 // do some stuffs
165 ...
166 return; <======= DO NOT DO THIS!
167 }
168 PJ_CATCH_ANY {
169 }
170 PJ_END;
171 }
172 \endverbatim
173
174 * - You can not provide more than PJ_CATCH or PJ_CATCH_ANY nor use PJ_CATCH
175 * and PJ_CATCH_ANY for a single PJ_TRY.
176 * - Exceptions will always be caught by the first handler (unlike C++ where
177 * exception is only caught if the type matches.
178
Benny Prijono9033e312005-11-21 02:08:39 +0000179 * \section PJ_EX_KEYWORDS Keywords
180 *
181 * \subsection PJ_THROW PJ_THROW(expression)
182 * Throw an exception. The expression thrown is an integer as the result of
183 * the \a expression. This keyword can be specified anywhere within the
184 * program.
185 *
186 * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION
187 * Specify this in the variable definition section of the function block
188 * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception
189 * block.
190 * Actually, this is just a macro to declare local variable which is used to
191 * push the exception state to the exception stack.
Benny Prijonod0d44f52005-11-21 16:57:02 +0000192 * Note: you must specify PJ_USE_EXCEPTION as the last statement in the
193 * local variable declarations, since it may evaluate to nothing.
Benny Prijono9033e312005-11-21 02:08:39 +0000194 *
195 * \subsection PJ_TRY PJ_TRY
196 * The \a PJ_TRY keyword is typically followed by a block. If an exception is
197 * thrown in this block, then the execution will resume to the \a PJ_CATCH
198 * handler.
199 *
200 * \subsection PJ_CATCH PJ_CATCH(expression)
201 * The \a PJ_CATCH is normally followed by a block. This block will be executed
202 * if the exception being thrown is equal to the expression specified in the
203 * \a PJ_CATCH.
204 *
Benny Prijonod0d44f52005-11-21 16:57:02 +0000205 * \subsection PJ_CATCH_ANY PJ_CATCH_ANY
206 * The \a PJ_CATCH is normally followed by a block. This block will be executed
207 * if any exception was raised in the TRY block.
Benny Prijono9033e312005-11-21 02:08:39 +0000208 *
209 * \subsection PJ_END PJ_END
210 * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks.
211 *
212 * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void)
213 * Get the last exception thrown. This macro is normally called inside the
Benny Prijonod0d44f52005-11-21 16:57:02 +0000214 * \a PJ_CATCH or \a PJ_CATCH_ANY block, altough it can be used anywhere where
Benny Prijono9033e312005-11-21 02:08:39 +0000215 * the \a PJ_USE_EXCEPTION definition is in scope.
216 *
217 *
218 * \section pj_except_examples_sec Examples
219 *
220 * For some examples on how to use the exception construct, please see:
221 * - @ref page_pjlib_samples_except_c
222 * - @ref page_pjlib_exception_test
223 */
224
225/**
226 * Allocate a unique exception id.
227 * Applications don't have to allocate a unique exception ID before using
228 * the exception construct. However, by doing so it ensures that there is
229 * no collisions of exception ID.
230 *
231 * As a bonus, when exception number is acquired through this function,
232 * the library can assign name to the exception (only if
233 * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the
234 * exception name when it catches an exception.
235 *
236 * @param name Name to be associated with the exception ID.
237 * @param id Pointer to receive the ID.
238 *
239 * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library
240 * is running out out ids.
241 */
242PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name,
243 pj_exception_id_t *id);
244
245/**
246 * Free an exception id.
247 *
248 * @param id The exception ID.
249 *
250 * @return PJ_SUCCESS or the appropriate error code.
251 */
252PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id);
253
254/**
255 * Retrieve name associated with the exception id.
256 *
257 * @param id The exception ID.
258 *
259 * @return The name associated with the specified ID.
260 */
261PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id);
262
263
264/** @} */
265
Benny Prijonod0d44f52005-11-21 16:57:02 +0000266#if defined(PJ_EXCEPTION_USE_WIN32_SEH) && PJ_EXCEPTION_USE_WIN32_SEH != 0
267/*****************************************************************************
268 **
269 ** IMPLEMENTATION OF EXCEPTION USING WINDOWS SEH
270 **
271 ****************************************************************************/
272#define WIN32_LEAN_AND_MEAN
273#include <windows.h>
274
275PJ_IDECL_NO_RETURN(void)
276pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN
277{
278 RaiseException(id,1,0,NULL);
279}
280
281#define PJ_USE_EXCEPTION
282#define PJ_TRY __try
283#define PJ_CATCH(id) __except(GetExceptionCode()==id ? \
284 EXCEPTION_EXECUTE_HANDLER : \
285 EXCEPTION_CONTINUE_SEARCH)
286#define PJ_CATCH_ANY __except(EXCEPTION_EXECUTE_HANDLER)
287#define PJ_END
288#define PJ_THROW(id) pj_throw_exception_(id)
289#define PJ_GET_EXCEPTION() GetExceptionCode()
290
Benny Prijonof260e462007-04-30 21:03:32 +0000291
292#elif defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
293/*****************************************************************************
294 **
295 ** IMPLEMENTATION OF EXCEPTION USING SYMBIAN LEAVE/TRAP FRAMEWORK
296 **
297 ****************************************************************************/
298
Benny Prijonoba5926a2007-05-02 11:29:37 +0000299/* To include this file, the source file must be compiled as
300 * C++ code!
301 */
302#ifdef __cplusplus
303
Benny Prijonof260e462007-04-30 21:03:32 +0000304class TPjException
305{
306public:
307 int code_;
308};
309
310#define PJ_USE_EXCEPTION
311#define PJ_TRY try
312//#define PJ_CATCH(id)
313#define PJ_CATCH_ANY catch (const TPjException & pj_excp_)
314#define PJ_END
315#define PJ_THROW(x_id) do { TPjException e; e.code_=x_id; throw e;} \
316 while (0)
317#define PJ_GET_EXCEPTION() pj_excp_.code_
318
Benny Prijonoa0c8b5c2007-05-12 15:03:23 +0000319#else
320
321#define PJ_USE_EXCEPTION
322#define PJ_TRY
323#define PJ_CATCH_ANY if (0)
324#define PJ_END
325#define PJ_THROW(x_id) do { PJ_LOG(1,("PJ_THROW"," error code = %d",x_id)); } while (0)
326#define PJ_GET_EXCEPTION() 0
327
Benny Prijonoba5926a2007-05-02 11:29:37 +0000328#endif /* __cplusplus */
329
Benny Prijonod0d44f52005-11-21 16:57:02 +0000330#else
331/*****************************************************************************
332 **
333 ** IMPLEMENTATION OF EXCEPTION USING GENERIC SETJMP/LONGJMP
334 **
335 ****************************************************************************/
336
Benny Prijono9033e312005-11-21 02:08:39 +0000337/**
338 * This structure (which should be invisible to user) manages the TRY handler
339 * stack.
340 */
341struct pj_exception_state_t
342{
343 struct pj_exception_state_t *prev; /**< Previous state in the list. */
344 pj_jmp_buf state; /**< jmp_buf. */
345};
346
347/**
348 * Throw exception.
349 * @param id Exception Id.
350 */
351PJ_DECL_NO_RETURN(void)
352pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN;
353
354/**
355 * Push exception handler.
356 */
357PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec);
358
359/**
360 * Pop exception handler.
361 */
Benny Prijonof561b7e2009-08-14 10:41:00 +0000362PJ_DECL(void) pj_pop_exception_handler_(struct pj_exception_state_t *rec);
Benny Prijono9033e312005-11-21 02:08:39 +0000363
364/**
365 * Declare that the function will use exception.
366 * @hideinitializer
367 */
368#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__
369
370/**
371 * Start exception specification block.
372 * @hideinitializer
373 */
374#define PJ_TRY if (1) { \
375 pj_push_exception_handler_(&pj_x_except__); \
376 pj_x_code__ = pj_setjmp(pj_x_except__.state); \
377 if (pj_x_code__ == 0)
378/**
379 * Catch the specified exception Id.
380 * @param id The exception number to catch.
381 * @hideinitializer
382 */
383#define PJ_CATCH(id) else if (pj_x_code__ == (id))
384
385/**
386 * Catch any exception number.
387 * @hideinitializer
388 */
Benny Prijonod0d44f52005-11-21 16:57:02 +0000389#define PJ_CATCH_ANY else
Benny Prijono9033e312005-11-21 02:08:39 +0000390
391/**
392 * End of exception specification block.
393 * @hideinitializer
394 */
Benny Prijonof561b7e2009-08-14 10:41:00 +0000395#define PJ_END pj_pop_exception_handler_(&pj_x_except__); \
Benny Prijono9033e312005-11-21 02:08:39 +0000396 } else {}
397
398/**
399 * Throw exception.
400 * @param exception_id The exception number.
401 * @hideinitializer
402 */
403#define PJ_THROW(exception_id) pj_throw_exception_(exception_id)
404
405/**
406 * Get current exception.
407 * @return Current exception code.
408 * @hideinitializer
409 */
410#define PJ_GET_EXCEPTION() (pj_x_code__)
411
Benny Prijonod0d44f52005-11-21 16:57:02 +0000412#endif /* PJ_EXCEPTION_USE_WIN32_SEH */
413
414
Benny Prijono9033e312005-11-21 02:08:39 +0000415PJ_END_DECL
416
417
418
419#endif /* __PJ_EXCEPTION_H__ */
420
421