blob: e5a8c899a7f42713db83157b7db9134e2267a3b4 [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 *
60 * CAVEATS:
61 * - unlike C++ exception, the scheme here won't call destructors of local
62 * objects if exception is thrown. Care must be taken when a function
63 * hold some resorce such as pool or mutex etc.
64 * - You CAN NOT make nested exception in one single function without using
65 * a nested PJ_USE_EXCEPTION.
Benny Prijonod0d44f52005-11-21 16:57:02 +000066 * - You can not provide more than PJ_CATCH or PJ_CATCH_ANY nor use PJ_CATCH
67 * and PJ_CATCH_ANY for a single PJ_TRY.
68 * - Exceptions will always be caught by the first handler (unlike C++ where
Benny Prijono9033e312005-11-21 02:08:39 +000069 * exception is only caught if the type matches.
70 *
71 * The exception handling constructs are similar to C++. The blocks will be
72 * constructed similar to the following sample:
73 *
74 * \verbatim
75 #define NO_MEMORY 1
76 #define SYNTAX_ERROR 2
77
Benny Prijonod0d44f52005-11-21 16:57:02 +000078 int sample1()
Benny Prijono9033e312005-11-21 02:08:39 +000079 {
80 PJ_USE_EXCEPTION; // declare local exception stack.
81
82 PJ_TRY {
83 ...// do something..
84 }
85 PJ_CATCH(NO_MEMORY) {
86 ... // handle exception 1
87 }
Benny Prijonod0d44f52005-11-21 16:57:02 +000088 PJ_END;
89 }
90
91 int sample2()
92 {
93 PJ_USE_EXCEPTION; // declare local exception stack.
94
95 PJ_TRY {
96 ...// do something..
Benny Prijono9033e312005-11-21 02:08:39 +000097 }
Benny Prijonod0d44f52005-11-21 16:57:02 +000098 PJ_CATCH_ANY {
99 if (PJ_GET_EXCEPTION() == NO_MEMORY)
100 ...; // handle no memory situation
101 else if (PJ_GET_EXCEPTION() == SYNTAX_ERROR)
102 ...; // handle syntax error
Benny Prijono9033e312005-11-21 02:08:39 +0000103 }
104 PJ_END;
105 }
106 \endverbatim
107 *
108 * The above sample uses hard coded exception ID. It is @b strongly
109 * recommended that applications request a unique exception ID instead
110 * of hard coded value like above.
111 *
112 * \section pj_except_reg Exception ID Allocation
113 *
114 * To ensure that exception ID (number) are used consistently and to
115 * prevent ID collisions in an application, it is strongly suggested that
116 * applications allocate an exception ID for each possible exception
117 * type. As a bonus of this process, the application can identify
118 * the name of the exception when the particular exception is thrown.
119 *
120 * Exception ID management are performed with the following APIs:
121 * - #pj_exception_id_alloc().
122 * - #pj_exception_id_free().
123 * - #pj_exception_id_name().
124 *
125 *
126 * PJLIB itself automatically allocates one exception id, i.e.
127 * #PJ_NO_MEMORY_EXCEPTION which is declared in <pj/pool.h>. This exception
128 * ID is raised by default pool policy when it fails to allocate memory.
129 *
130 * \section PJ_EX_KEYWORDS Keywords
131 *
132 * \subsection PJ_THROW PJ_THROW(expression)
133 * Throw an exception. The expression thrown is an integer as the result of
134 * the \a expression. This keyword can be specified anywhere within the
135 * program.
136 *
137 * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION
138 * Specify this in the variable definition section of the function block
139 * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception
140 * block.
141 * Actually, this is just a macro to declare local variable which is used to
142 * push the exception state to the exception stack.
Benny Prijonod0d44f52005-11-21 16:57:02 +0000143 * Note: you must specify PJ_USE_EXCEPTION as the last statement in the
144 * local variable declarations, since it may evaluate to nothing.
Benny Prijono9033e312005-11-21 02:08:39 +0000145 *
146 * \subsection PJ_TRY PJ_TRY
147 * The \a PJ_TRY keyword is typically followed by a block. If an exception is
148 * thrown in this block, then the execution will resume to the \a PJ_CATCH
149 * handler.
150 *
151 * \subsection PJ_CATCH PJ_CATCH(expression)
152 * The \a PJ_CATCH is normally followed by a block. This block will be executed
153 * if the exception being thrown is equal to the expression specified in the
154 * \a PJ_CATCH.
155 *
Benny Prijonod0d44f52005-11-21 16:57:02 +0000156 * \subsection PJ_CATCH_ANY PJ_CATCH_ANY
157 * The \a PJ_CATCH is normally followed by a block. This block will be executed
158 * if any exception was raised in the TRY block.
Benny Prijono9033e312005-11-21 02:08:39 +0000159 *
160 * \subsection PJ_END PJ_END
161 * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks.
162 *
163 * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void)
164 * Get the last exception thrown. This macro is normally called inside the
Benny Prijonod0d44f52005-11-21 16:57:02 +0000165 * \a PJ_CATCH or \a PJ_CATCH_ANY block, altough it can be used anywhere where
Benny Prijono9033e312005-11-21 02:08:39 +0000166 * the \a PJ_USE_EXCEPTION definition is in scope.
167 *
168 *
169 * \section pj_except_examples_sec Examples
170 *
171 * For some examples on how to use the exception construct, please see:
172 * - @ref page_pjlib_samples_except_c
173 * - @ref page_pjlib_exception_test
174 */
175
176/**
177 * Allocate a unique exception id.
178 * Applications don't have to allocate a unique exception ID before using
179 * the exception construct. However, by doing so it ensures that there is
180 * no collisions of exception ID.
181 *
182 * As a bonus, when exception number is acquired through this function,
183 * the library can assign name to the exception (only if
184 * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the
185 * exception name when it catches an exception.
186 *
187 * @param name Name to be associated with the exception ID.
188 * @param id Pointer to receive the ID.
189 *
190 * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library
191 * is running out out ids.
192 */
193PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name,
194 pj_exception_id_t *id);
195
196/**
197 * Free an exception id.
198 *
199 * @param id The exception ID.
200 *
201 * @return PJ_SUCCESS or the appropriate error code.
202 */
203PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id);
204
205/**
206 * Retrieve name associated with the exception id.
207 *
208 * @param id The exception ID.
209 *
210 * @return The name associated with the specified ID.
211 */
212PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id);
213
214
215/** @} */
216
Benny Prijonod0d44f52005-11-21 16:57:02 +0000217#if defined(PJ_EXCEPTION_USE_WIN32_SEH) && PJ_EXCEPTION_USE_WIN32_SEH != 0
218/*****************************************************************************
219 **
220 ** IMPLEMENTATION OF EXCEPTION USING WINDOWS SEH
221 **
222 ****************************************************************************/
223#define WIN32_LEAN_AND_MEAN
224#include <windows.h>
225
226PJ_IDECL_NO_RETURN(void)
227pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN
228{
229 RaiseException(id,1,0,NULL);
230}
231
232#define PJ_USE_EXCEPTION
233#define PJ_TRY __try
234#define PJ_CATCH(id) __except(GetExceptionCode()==id ? \
235 EXCEPTION_EXECUTE_HANDLER : \
236 EXCEPTION_CONTINUE_SEARCH)
237#define PJ_CATCH_ANY __except(EXCEPTION_EXECUTE_HANDLER)
238#define PJ_END
239#define PJ_THROW(id) pj_throw_exception_(id)
240#define PJ_GET_EXCEPTION() GetExceptionCode()
241
Benny Prijonof260e462007-04-30 21:03:32 +0000242
243#elif defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
244/*****************************************************************************
245 **
246 ** IMPLEMENTATION OF EXCEPTION USING SYMBIAN LEAVE/TRAP FRAMEWORK
247 **
248 ****************************************************************************/
249
Benny Prijonoba5926a2007-05-02 11:29:37 +0000250/* To include this file, the source file must be compiled as
251 * C++ code!
252 */
253#ifdef __cplusplus
254
Benny Prijonof260e462007-04-30 21:03:32 +0000255class TPjException
256{
257public:
258 int code_;
259};
260
261#define PJ_USE_EXCEPTION
262#define PJ_TRY try
263//#define PJ_CATCH(id)
264#define PJ_CATCH_ANY catch (const TPjException & pj_excp_)
265#define PJ_END
266#define PJ_THROW(x_id) do { TPjException e; e.code_=x_id; throw e;} \
267 while (0)
268#define PJ_GET_EXCEPTION() pj_excp_.code_
269
Benny Prijonoa0c8b5c2007-05-12 15:03:23 +0000270#else
271
272#define PJ_USE_EXCEPTION
273#define PJ_TRY
274#define PJ_CATCH_ANY if (0)
275#define PJ_END
276#define PJ_THROW(x_id) do { PJ_LOG(1,("PJ_THROW"," error code = %d",x_id)); } while (0)
277#define PJ_GET_EXCEPTION() 0
278
Benny Prijonoba5926a2007-05-02 11:29:37 +0000279#endif /* __cplusplus */
280
Benny Prijonod0d44f52005-11-21 16:57:02 +0000281#else
282/*****************************************************************************
283 **
284 ** IMPLEMENTATION OF EXCEPTION USING GENERIC SETJMP/LONGJMP
285 **
286 ****************************************************************************/
287
Benny Prijono9033e312005-11-21 02:08:39 +0000288/**
289 * This structure (which should be invisible to user) manages the TRY handler
290 * stack.
291 */
292struct pj_exception_state_t
293{
294 struct pj_exception_state_t *prev; /**< Previous state in the list. */
295 pj_jmp_buf state; /**< jmp_buf. */
296};
297
298/**
299 * Throw exception.
300 * @param id Exception Id.
301 */
302PJ_DECL_NO_RETURN(void)
303pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN;
304
305/**
306 * Push exception handler.
307 */
308PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec);
309
310/**
311 * Pop exception handler.
312 */
313PJ_DECL(void) pj_pop_exception_handler_(void);
314
315/**
316 * Declare that the function will use exception.
317 * @hideinitializer
318 */
319#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__
320
321/**
322 * Start exception specification block.
323 * @hideinitializer
324 */
325#define PJ_TRY if (1) { \
326 pj_push_exception_handler_(&pj_x_except__); \
327 pj_x_code__ = pj_setjmp(pj_x_except__.state); \
328 if (pj_x_code__ == 0)
329/**
330 * Catch the specified exception Id.
331 * @param id The exception number to catch.
332 * @hideinitializer
333 */
334#define PJ_CATCH(id) else if (pj_x_code__ == (id))
335
336/**
337 * Catch any exception number.
338 * @hideinitializer
339 */
Benny Prijonod0d44f52005-11-21 16:57:02 +0000340#define PJ_CATCH_ANY else
Benny Prijono9033e312005-11-21 02:08:39 +0000341
342/**
343 * End of exception specification block.
344 * @hideinitializer
345 */
346#define PJ_END pj_pop_exception_handler_(); \
347 } else {}
348
349/**
350 * Throw exception.
351 * @param exception_id The exception number.
352 * @hideinitializer
353 */
354#define PJ_THROW(exception_id) pj_throw_exception_(exception_id)
355
356/**
357 * Get current exception.
358 * @return Current exception code.
359 * @hideinitializer
360 */
361#define PJ_GET_EXCEPTION() (pj_x_code__)
362
Benny Prijonod0d44f52005-11-21 16:57:02 +0000363#endif /* PJ_EXCEPTION_USE_WIN32_SEH */
364
365
Benny Prijono9033e312005-11-21 02:08:39 +0000366PJ_END_DECL
367
368
369
370#endif /* __PJ_EXCEPTION_H__ */
371
372