blob: 610f4626c2c3c17af4cc0e35d25d995eb0928407 [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>
29
30
31PJ_BEGIN_DECL
32
33
34/**
35 * @defgroup PJ_EXCEPT Exception Handling
36 * @ingroup PJ_MISC
37 * @{
38 *
39 * \section pj_except_sample_sec Quick Example
40 *
41 * For the impatient, take a look at some examples:
42 * - @ref page_pjlib_samples_except_c
43 * - @ref page_pjlib_exception_test
44 *
45 * \section pj_except_except Exception Handling
46 *
47 * This module provides exception handling syntactically similar to C++ in
Benny Prijonod0d44f52005-11-21 16:57:02 +000048 * C language. In Win32 systems, it uses Windows Structured Exception
49 * Handling (SEH) if macro PJ_EXCEPTION_USE_WIN32_SEH is non-zero.
50 * Otherwise it will use setjmp() and longjmp().
Benny Prijono9033e312005-11-21 02:08:39 +000051 *
Benny Prijonod0d44f52005-11-21 16:57:02 +000052 * On some platforms where setjmp/longjmp is not available, setjmp/longjmp
53 * implementation is provided. See <pj/compat/setjmp.h> for compatibility.
Benny Prijono9033e312005-11-21 02:08:39 +000054 *
55 * The exception handling mechanism is completely thread safe, so the exception
56 * thrown by one thread will not interfere with other thread.
57 *
58 * CAVEATS:
59 * - unlike C++ exception, the scheme here won't call destructors of local
60 * objects if exception is thrown. Care must be taken when a function
61 * hold some resorce such as pool or mutex etc.
62 * - You CAN NOT make nested exception in one single function without using
63 * a nested PJ_USE_EXCEPTION.
Benny Prijonod0d44f52005-11-21 16:57:02 +000064 * - You can not provide more than PJ_CATCH or PJ_CATCH_ANY nor use PJ_CATCH
65 * and PJ_CATCH_ANY for a single PJ_TRY.
66 * - Exceptions will always be caught by the first handler (unlike C++ where
Benny Prijono9033e312005-11-21 02:08:39 +000067 * exception is only caught if the type matches.
68 *
69 * The exception handling constructs are similar to C++. The blocks will be
70 * constructed similar to the following sample:
71 *
72 * \verbatim
73 #define NO_MEMORY 1
74 #define SYNTAX_ERROR 2
75
Benny Prijonod0d44f52005-11-21 16:57:02 +000076 int sample1()
Benny Prijono9033e312005-11-21 02:08:39 +000077 {
78 PJ_USE_EXCEPTION; // declare local exception stack.
79
80 PJ_TRY {
81 ...// do something..
82 }
83 PJ_CATCH(NO_MEMORY) {
84 ... // handle exception 1
85 }
Benny Prijonod0d44f52005-11-21 16:57:02 +000086 PJ_END;
87 }
88
89 int sample2()
90 {
91 PJ_USE_EXCEPTION; // declare local exception stack.
92
93 PJ_TRY {
94 ...// do something..
Benny Prijono9033e312005-11-21 02:08:39 +000095 }
Benny Prijonod0d44f52005-11-21 16:57:02 +000096 PJ_CATCH_ANY {
97 if (PJ_GET_EXCEPTION() == NO_MEMORY)
98 ...; // handle no memory situation
99 else if (PJ_GET_EXCEPTION() == SYNTAX_ERROR)
100 ...; // handle syntax error
Benny Prijono9033e312005-11-21 02:08:39 +0000101 }
102 PJ_END;
103 }
104 \endverbatim
105 *
106 * The above sample uses hard coded exception ID. It is @b strongly
107 * recommended that applications request a unique exception ID instead
108 * of hard coded value like above.
109 *
110 * \section pj_except_reg Exception ID Allocation
111 *
112 * To ensure that exception ID (number) are used consistently and to
113 * prevent ID collisions in an application, it is strongly suggested that
114 * applications allocate an exception ID for each possible exception
115 * type. As a bonus of this process, the application can identify
116 * the name of the exception when the particular exception is thrown.
117 *
118 * Exception ID management are performed with the following APIs:
119 * - #pj_exception_id_alloc().
120 * - #pj_exception_id_free().
121 * - #pj_exception_id_name().
122 *
123 *
124 * PJLIB itself automatically allocates one exception id, i.e.
125 * #PJ_NO_MEMORY_EXCEPTION which is declared in <pj/pool.h>. This exception
126 * ID is raised by default pool policy when it fails to allocate memory.
127 *
128 * \section PJ_EX_KEYWORDS Keywords
129 *
130 * \subsection PJ_THROW PJ_THROW(expression)
131 * Throw an exception. The expression thrown is an integer as the result of
132 * the \a expression. This keyword can be specified anywhere within the
133 * program.
134 *
135 * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION
136 * Specify this in the variable definition section of the function block
137 * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception
138 * block.
139 * Actually, this is just a macro to declare local variable which is used to
140 * push the exception state to the exception stack.
Benny Prijonod0d44f52005-11-21 16:57:02 +0000141 * Note: you must specify PJ_USE_EXCEPTION as the last statement in the
142 * local variable declarations, since it may evaluate to nothing.
Benny Prijono9033e312005-11-21 02:08:39 +0000143 *
144 * \subsection PJ_TRY PJ_TRY
145 * The \a PJ_TRY keyword is typically followed by a block. If an exception is
146 * thrown in this block, then the execution will resume to the \a PJ_CATCH
147 * handler.
148 *
149 * \subsection PJ_CATCH PJ_CATCH(expression)
150 * The \a PJ_CATCH is normally followed by a block. This block will be executed
151 * if the exception being thrown is equal to the expression specified in the
152 * \a PJ_CATCH.
153 *
Benny Prijonod0d44f52005-11-21 16:57:02 +0000154 * \subsection PJ_CATCH_ANY PJ_CATCH_ANY
155 * The \a PJ_CATCH is normally followed by a block. This block will be executed
156 * if any exception was raised in the TRY block.
Benny Prijono9033e312005-11-21 02:08:39 +0000157 *
158 * \subsection PJ_END PJ_END
159 * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks.
160 *
161 * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void)
162 * Get the last exception thrown. This macro is normally called inside the
Benny Prijonod0d44f52005-11-21 16:57:02 +0000163 * \a PJ_CATCH or \a PJ_CATCH_ANY block, altough it can be used anywhere where
Benny Prijono9033e312005-11-21 02:08:39 +0000164 * the \a PJ_USE_EXCEPTION definition is in scope.
165 *
166 *
167 * \section pj_except_examples_sec Examples
168 *
169 * For some examples on how to use the exception construct, please see:
170 * - @ref page_pjlib_samples_except_c
171 * - @ref page_pjlib_exception_test
172 */
173
174/**
175 * Allocate a unique exception id.
176 * Applications don't have to allocate a unique exception ID before using
177 * the exception construct. However, by doing so it ensures that there is
178 * no collisions of exception ID.
179 *
180 * As a bonus, when exception number is acquired through this function,
181 * the library can assign name to the exception (only if
182 * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the
183 * exception name when it catches an exception.
184 *
185 * @param name Name to be associated with the exception ID.
186 * @param id Pointer to receive the ID.
187 *
188 * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library
189 * is running out out ids.
190 */
191PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name,
192 pj_exception_id_t *id);
193
194/**
195 * Free an exception id.
196 *
197 * @param id The exception ID.
198 *
199 * @return PJ_SUCCESS or the appropriate error code.
200 */
201PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id);
202
203/**
204 * Retrieve name associated with the exception id.
205 *
206 * @param id The exception ID.
207 *
208 * @return The name associated with the specified ID.
209 */
210PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id);
211
212
213/** @} */
214
Benny Prijonod0d44f52005-11-21 16:57:02 +0000215#if defined(PJ_EXCEPTION_USE_WIN32_SEH) && PJ_EXCEPTION_USE_WIN32_SEH != 0
216/*****************************************************************************
217 **
218 ** IMPLEMENTATION OF EXCEPTION USING WINDOWS SEH
219 **
220 ****************************************************************************/
221#define WIN32_LEAN_AND_MEAN
222#include <windows.h>
223
224PJ_IDECL_NO_RETURN(void)
225pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN
226{
227 RaiseException(id,1,0,NULL);
228}
229
230#define PJ_USE_EXCEPTION
231#define PJ_TRY __try
232#define PJ_CATCH(id) __except(GetExceptionCode()==id ? \
233 EXCEPTION_EXECUTE_HANDLER : \
234 EXCEPTION_CONTINUE_SEARCH)
235#define PJ_CATCH_ANY __except(EXCEPTION_EXECUTE_HANDLER)
236#define PJ_END
237#define PJ_THROW(id) pj_throw_exception_(id)
238#define PJ_GET_EXCEPTION() GetExceptionCode()
239
Benny Prijonof260e462007-04-30 21:03:32 +0000240
241#elif defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
242/*****************************************************************************
243 **
244 ** IMPLEMENTATION OF EXCEPTION USING SYMBIAN LEAVE/TRAP FRAMEWORK
245 **
246 ****************************************************************************/
247
Benny Prijonoba5926a2007-05-02 11:29:37 +0000248/* To include this file, the source file must be compiled as
249 * C++ code!
250 */
251#ifdef __cplusplus
252
Benny Prijonof260e462007-04-30 21:03:32 +0000253class TPjException
254{
255public:
256 int code_;
257};
258
259#define PJ_USE_EXCEPTION
260#define PJ_TRY try
261//#define PJ_CATCH(id)
262#define PJ_CATCH_ANY catch (const TPjException & pj_excp_)
263#define PJ_END
264#define PJ_THROW(x_id) do { TPjException e; e.code_=x_id; throw e;} \
265 while (0)
266#define PJ_GET_EXCEPTION() pj_excp_.code_
267
Benny Prijonoba5926a2007-05-02 11:29:37 +0000268#endif /* __cplusplus */
269
Benny Prijonod0d44f52005-11-21 16:57:02 +0000270#else
271/*****************************************************************************
272 **
273 ** IMPLEMENTATION OF EXCEPTION USING GENERIC SETJMP/LONGJMP
274 **
275 ****************************************************************************/
276
Benny Prijono9033e312005-11-21 02:08:39 +0000277/**
278 * This structure (which should be invisible to user) manages the TRY handler
279 * stack.
280 */
281struct pj_exception_state_t
282{
283 struct pj_exception_state_t *prev; /**< Previous state in the list. */
284 pj_jmp_buf state; /**< jmp_buf. */
285};
286
287/**
288 * Throw exception.
289 * @param id Exception Id.
290 */
291PJ_DECL_NO_RETURN(void)
292pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN;
293
294/**
295 * Push exception handler.
296 */
297PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec);
298
299/**
300 * Pop exception handler.
301 */
302PJ_DECL(void) pj_pop_exception_handler_(void);
303
304/**
305 * Declare that the function will use exception.
306 * @hideinitializer
307 */
308#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__
309
310/**
311 * Start exception specification block.
312 * @hideinitializer
313 */
314#define PJ_TRY if (1) { \
315 pj_push_exception_handler_(&pj_x_except__); \
316 pj_x_code__ = pj_setjmp(pj_x_except__.state); \
317 if (pj_x_code__ == 0)
318/**
319 * Catch the specified exception Id.
320 * @param id The exception number to catch.
321 * @hideinitializer
322 */
323#define PJ_CATCH(id) else if (pj_x_code__ == (id))
324
325/**
326 * Catch any exception number.
327 * @hideinitializer
328 */
Benny Prijonod0d44f52005-11-21 16:57:02 +0000329#define PJ_CATCH_ANY else
Benny Prijono9033e312005-11-21 02:08:39 +0000330
331/**
332 * End of exception specification block.
333 * @hideinitializer
334 */
335#define PJ_END pj_pop_exception_handler_(); \
336 } else {}
337
338/**
339 * Throw exception.
340 * @param exception_id The exception number.
341 * @hideinitializer
342 */
343#define PJ_THROW(exception_id) pj_throw_exception_(exception_id)
344
345/**
346 * Get current exception.
347 * @return Current exception code.
348 * @hideinitializer
349 */
350#define PJ_GET_EXCEPTION() (pj_x_code__)
351
Benny Prijonod0d44f52005-11-21 16:57:02 +0000352#endif /* PJ_EXCEPTION_USE_WIN32_SEH */
353
354
Benny Prijono9033e312005-11-21 02:08:39 +0000355PJ_END_DECL
356
357
358
359#endif /* __PJ_EXCEPTION_H__ */
360
361