CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
ptry.h
Go to the documentation of this file.
1#pragma once
2
5
81
82#include <cx/meta/pblock.h>
83
88typedef struct ExceptionInfo {
89 int code;
90 const char* msg;
91#if DEBUG_LEVEL >= 1
92 const char* file;
93 int ln;
94#endif
96
97extern _Thread_local _pblock_jmp_buf_node* _ptry_top; // stack of jump buffers to rewind to
98extern _Thread_local ExceptionInfo _ptry_exc; // currently active exception
99extern ExceptionInfo _ptry_exc_empty;
100
105typedef int (*ptUnhandledHandler)(ExceptionInfo* einfo);
106
107// GCC falsely warns that _ptry_top is a dangling pointer, even though we're very careful to clean
108// it up when exiting the scope that the local variable it points to is declared in.
109#if defined(__GNUC__) && __GNUC__ >= 12
110#define ptDisablePtrWarning \
111 _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdangling-pointer\"")
112#define ptReenableWarnings _Pragma("GCC diagnostic pop")
113#else
114#define ptDisablePtrWarning
115#define ptReenableWarnings
116#endif
117
139
144
145void _ptry_handle_unhandled(ExceptionInfo* einfo);
146
147_meta_inline void _ptry_clear(void)
148{
149 _ptry_exc = _ptry_exc_empty;
150}
151
152_meta_inline void _ptry_push(_pblock_jmp_buf_node* node)
153{
154 node->next = _ptry_top;
155 ptDisablePtrWarning;
156 _ptry_top = node;
157 ptReenableWarnings;
158}
159
160_meta_inline _pblock_jmp_buf_node* _ptry_pop(void)
161{
162 _pblock_jmp_buf_node* ret = _ptry_top;
163 if (!ret)
164 return ret;
165
166 _ptry_top = ret->next;
167 ret->next = NULL;
168 return ret;
169}
170
171_meta_inline void _ptry_pop_until(_pblock_jmp_buf_node* node)
172{
173 _pblock_jmp_buf_node* last;
174 // keep popping until we hit the specified node or run out of jump buffers
175 do {
176 last = _ptry_pop();
177 } while (last && last != node);
178}
179
180#if DEBUG_LEVEL >= 1
181_meta_inline void _ptry_throw(int code, const char* msg, const char* file, int ln)
182#else
183_meta_inline void _ptry_throw(int code, const char* msg)
184#endif
185{
186 // populate the thread-local exception info as we go into exception-handling mode
187 _ptry_exc.code = code;
188 _ptry_exc.msg = msg;
189#if DEBUG_LEVEL >= 1
190 _ptry_exc.file = file;
191 _ptry_exc.ln = ln;
192#endif
193
194 // jump to the handler block if we have one, in most cases this function stops executing here
195 if (_ptry_top)
196 _pbLongjmp(_ptry_top, -1);
197
198 // otherwise call the unhandled exception handler, this will probably abort and never return
199 _ptry_handle_unhandled(&_ptry_exc);
200
201 // if it DOES return, terminate exception processing
202 _ptry_clear();
203}
204
236#if DEBUG_LEVEL >= 1
237#define ptThrow(code, msg) _ptry_throw(code, msg, __FILE__, __LINE__)
238#else
239#define ptThrow(code, msg) _ptry_throw(code, msg)
240#endif
241
272#if DEBUG_LEVEL >= 1
273#define ptRethrow _ptry_throw(_ptry_exc.code, _ptry_exc.msg, _ptry_exc.file, _ptry_exc.ln)
274#else
275#define ptRethrow _ptry_throw(_ptry_exc.code, _ptry_exc.msg)
276#endif
277
309#define ptTry \
310 pblock /* phase 0 is the try, phase 1 is the catch/finally */ \
311 for (unsigned _ptry_phase = 0; _ptry_phase < 2; ++_ptry_phase) /* try phase */ \
312 if (!_ptry_phase) /* push the current pblock jump target to the exception handler stack */ /* this will cause it to be entered through the switch label */ \
313 _blkBefore(_ptry_push(_pblock_unwind_top)) do
314
315// common setup for catch/finally
316#define _ptry_after_block \
317 /* this can be reached two ways, either naturally in phase 1 of the _ptry_phase loop (this is \
318 * the 'else'), or if an exception occurs, through the case 1: label through the pblock after \
319 * longjmping into the pblock loop from a lower stack frame. The latter is the same mechanism \
320 * that PBLOCK_AFTER would use, but for try/catch it's unconditional. */ \
321 while (0); \
322 else case 1: \
323 /* critically important to protect the outer block loop variable here! */ \
324 _blkStart /* make sure that this phase isn't executed again (if we got here via longjmp) */ \
325 _blkBefore(_ptry_phase = 2) /* unwind the handler stack to one level above where we currently \
326 are */ \
327 _blkBefore(_ptry_pop_until(_pblock_unwind_top))
328
356#define ptFinally \
357 _ptry_after_block /* finally blocks end in an implicit rethrow if there is an active exception \
358 */ \
359 _blkAfter(_ptry_exc.code ? ptRethrow : nop_stmt)
360
406#define ptCatch _ptry_after_block _blkAfter(_ptry_clear())
407
429#define ptCode _ptry_exc.code
430
449#define ptMsg _ptry_exc.msg
450
void ptUnregisterUnhandled(ptUnhandledHandler handler)
void ptRegisterUnhandled(ptUnhandledHandler handler)
int(* ptUnhandledHandler)(ExceptionInfo *einfo)
Definition ptry.h:105
Protected blocks with stack unwinding (advanced feature)
int code
Application-specific exception code.
Definition ptry.h:89
const char * msg
Exception message - MUST be a static string literal.
Definition ptry.h:90