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