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 _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdangling-pointer\"")
111#define ptReenableWarnings _Pragma("GCC diagnostic pop")
112#else
113#define ptDisablePtrWarning
114#define ptReenableWarnings
115#endif
116
138
143
144void _ptry_handle_unhandled(ExceptionInfo *einfo);
145
146_meta_inline void _ptry_clear(void)
147{
148 _ptry_exc = _ptry_exc_empty;
149}
150
151_meta_inline void _ptry_push(_pblock_jmp_buf_node *node)
152{
153 node->next = _ptry_top;
154 ptDisablePtrWarning;
155 _ptry_top = node;
156 ptReenableWarnings;
157}
158
159_meta_inline _pblock_jmp_buf_node *_ptry_pop(void)
160{
161 _pblock_jmp_buf_node *ret = _ptry_top;
162 if (!ret)
163 return ret;
164
165 _ptry_top = ret->next;
166 ret->next = NULL;
167 return ret;
168}
169
170_meta_inline void _ptry_pop_until(_pblock_jmp_buf_node *node)
171{
172 _pblock_jmp_buf_node *last;
173 // keep popping until we hit the specified node or run out of jump buffers
174 do {
175 last = _ptry_pop();
176 } while (last && last != node);
177}
178
179#if DEBUG_LEVEL >= 1
180_meta_inline void _ptry_throw(int code, const char *msg, const char *file, int ln)
181#else
182_meta_inline void _ptry_throw(int code, const char *msg)
183#endif
184{
185 // populate the thread-local exception info as we go into exception-handling mode
186 _ptry_exc.code = code;
187 _ptry_exc.msg = msg;
188#if DEBUG_LEVEL >= 1
189 _ptry_exc.file = file;
190 _ptry_exc.ln = ln;
191#endif
192
193 // jump to the handler block if we have one, in most cases this function stops executing here
194 if (_ptry_top) _pbLongjmp(_ptry_top, -1);
195
196 // otherwise call the unhandled exception handler, this will probably abort and never return
197 _ptry_handle_unhandled(&_ptry_exc);
198
199 // if it DOES return, terminate exception processing
200 _ptry_clear();
201}
202
234#if DEBUG_LEVEL >= 1
235#define ptThrow(code, msg) _ptry_throw(code, msg, __FILE__, __LINE__)
236#else
237#define ptThrow(code, msg) _ptry_throw(code, msg)
238#endif
239
270#if DEBUG_LEVEL >= 1
271#define ptRethrow _ptry_throw(_ptry_exc.code, _ptry_exc.msg, _ptry_exc.file, _ptry_exc.ln)
272#else
273#define ptRethrow _ptry_throw(_ptry_exc.code, _ptry_exc.msg)
274#endif
275
307#define ptTry \
308 pblock \
309 /* phase 0 is the try, phase 1 is the catch/finally */ \
310 for (unsigned _ptry_phase = 0; _ptry_phase < 2; ++_ptry_phase) \
311 /* try phase */ \
312 if (!_ptry_phase) \
313 /* push the current pblock jump target to the exception handler stack */ \
314 /* this will cause it to be entered through the switch label */ \
315 _blkBefore(_ptry_push(_pblock_unwind_top)) \
316 do
317
318// common setup for catch/finally
319#define _ptry_after_block \
320 /* this can be reached two ways, either naturally in phase 1 of the _ptry_phase loop (this is \
321 * the 'else'), or if an exception occurs, through the case 1: label through the pblock after \
322 * longjmping into the pblock loop from a lower stack frame. The latter is the same mechanism \
323 * that PBLOCK_AFTER would use, but for try/catch it's unconditional. */ \
324 while(0); else case 1: \
325 /* critically important to protect the outer block loop variable here! */ \
326 _blkStart \
327 /* make sure that this phase isn't executed again (if we got here via longjmp) */ \
328 _blkBefore(_ptry_phase = 2) \
329 /* unwind the handler stack to one level above where we currently are */ \
330 _blkBefore(_ptry_pop_until(_pblock_unwind_top))
331
359#define ptFinally \
360 _ptry_after_block \
361 /* finally blocks end in an implicit rethrow if there is an active exception */ \
362 _blkAfter(_ptry_exc.code ? ptRethrow : nop_stmt)
363
409#define ptCatch \
410 _ptry_after_block \
411 _blkAfter(_ptry_clear())
412
434#define ptCode _ptry_exc.code
435
454#define ptMsg _ptry_exc.msg
455
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