P99
p99_try.h
Go to the documentation of this file.
1 /* This may look like nonsense, but it really is -*- mode: C; coding: utf-8 -*- */
2 /* */
3 /* Except for parts copied from previous work and as explicitly stated below, */
4 /* the authors and copyright holders for this work are as follows: */
5 /* (C) copyright 2012-2014 Jens Gustedt, INRIA, France */
6 /* (C) copyright 2012 Simon Peeters */
7 /* (C) copyright 2012 William Morris */
8 /* */
9 /* This file is free software; it is part of the P99 project. */
10 /* */
11 /* Licensed under the Apache License, Version 2.0 (the "License"); */
12 /* you may not use this file except in compliance with the License. */
13 /* You may obtain a copy of the License at */
14 /* */
15 /* http://www.apache.org/licenses/LICENSE-2.0 */
16 /* */
17 /* Unless required by applicable law or agreed to in writing, software */
18 /* distributed under the License is distributed on an "AS IS" BASIS, */
19 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
20 /* See the License for the specific language governing permissions and */
21 /* limitations under the License. */
22 /* */
23 #ifndef P99_TRY_H
24 #define P99_TRY_H
25 #include "p99_constraint.h"
26 #include "p99_lifo.h"
27 
28 P99_POINTER_TYPE(p00_jmp_buf0);
29 P99_LIFO_DECLARE(p00_jmp_buf0_ptr);
30 
31 P99_DECLARE_THREAD_LOCAL(P99_LIFO(p00_jmp_buf0_ptr), p00_jmp_buf_top);
32 
33 #define P00_JMP_BUF_TOP P99_THREAD_LOCAL(p00_jmp_buf_top)
34 
36 void p00_jmp_skip(p00_jmp_buf0 * p00_des) {
37  P99_LIFO(p00_jmp_buf0_ptr)* p00_head = &P00_JMP_BUF_TOP;
38  p00_jmp_buf0 * p00_ret = 0;
39  do {
40  p00_ret = P99_LIFO_POP(p00_head);
41  } while (p00_ret && (p00_ret != p00_des));
42 }
43 
45 void p00_jmp_push(p00_jmp_buf0 * p00_des) {
46  P99_LIFO_PUSH(&P00_JMP_BUF_TOP, p00_des);
47 }
48 
71  p00_constraint_report(p00_cond, 0, 0, 0);
72 }
73 
76 void p00_jmp_abort(errno_t p00_cond, char const* p00_file, char const* p00_context, char const* p00_info) {
77  p00_constraint_report(p00_cond, p00_file, p00_context, p00_info);
78  constraint_handler_t p00_func = atomic_load(&p00_constraint_handler);
79  if (p00_func == exit_handler_s) {
80  /* Avoid looping in exit handlers. */
81  set_constraint_handler_s(abort_handler_s);
82  /* Give the application a chance to clean up. */
83  exit(EXIT_FAILURE);
84  }
85  abort();
86 }
87 
90 void p00_jmp_throw(errno_t p00_cond, p00_jmp_buf0 * p00_top, char const* p00_file, char const* p00_context, char const* p00_info) {
91  if (p00_file) P00_JMP_BUF_FILE = p00_file;
92  if (p00_context) P00_JMP_BUF_CONTEXT = p00_context;
93  if (p00_info) P00_JMP_BUF_INFO = p00_info;
94  if (!p00_top) p00_top = P99_LIFO_TOP(&P00_JMP_BUF_TOP);
95  if (P99_LIKELY(p00_top)) p00_longjmp(p00_top, p00_cond);
96  else p00_jmp_abort(p00_cond, p00_file, p00_context, p00_info);
97 }
98 
99 
127 P00_UNWIND_DOCUMENT
128 #define P99_THROW(X) p00_jmp_throw((X), p00_unwind_top, P99_STRINGIFY(__LINE__), __func__, "throw")
129 
131 noreturn
132 void p00_throw_errno(p00_jmp_buf0 * p00_top, errno_t p00_def, char const* p00_file, char const* p00_context, char const* p00_info) {
133  errno_t p00_err = errno;
134  if (!p00_err) p00_err = (p00_def ? p00_def : EINVAL);
135  errno = 0;
136  p00_jmp_throw(p00_err, p00_top, p00_file, p00_context, p00_info);
137 }
138 
140 void p00_throw_call_range(p00_jmp_buf0 * p00_top, errno_t p00_sign, char const* p00_file, char const* p00_context, char const* p00_info) {
141  errno_t p00_err = errno;
142  if (P99_LIKELY(!p00_err)) return;
143  switch (p00_err) {
144  case ERANGE: if (p00_sign) p00_err = p00_sign;
145  }
146  errno = 0;
147  p00_jmp_throw(p00_err, p00_top, p00_file, p00_context, p00_info);
148 }
149 
150 #define P00_ALMOST_ZERO(X) \
151 P99_GENERIC((X), \
152  ((X) > 0 ? (X) <= FLT_MIN : -(X) >= FLT_MIN), \
153  (float, ((X) > 0 ? (X) <= LDBL_MIN : -(X) >= LDBL_MIN)), \
154  (long double, ((X) > 0 ? (X) <= LDBL_MIN : -(X) >= LDBL_MIN)) \
155  )
156 
157 /* Define a function for each of the standard real types, that runs
158  p00_throw_call_range with the correct information of under- or
159  overflow. We need this for compilers that don't support gcc's block
160  expressions and @c typeof. */
161 #define P00_THROW_CALL_RANGE(T, F, ...) \
162 p99_inline \
163 T P99_PASTE2(p00_throw_call_range_, T)(p00_jmp_buf0 * p00_top, \
164  T p00_val, \
165  char const* p00_file, \
166  char const* p00_context, \
167  char const* p00_info) { \
168  if (P99_UNLIKELY(P99_IS_ONE(p00_val, 0, __VA_ARGS__)) \
169  P99_IF_EQ_1(F)(|| P99_UNLIKELY(P00_ALMOST_ZERO(p00_val)))() \
170  ) \
171  p00_throw_call_range(p00_top, \
172  /* also capture errors for floating \
173  types, for integer types part of this \
174  is redundant. */ \
175  ((p00_val > 0) \
176  ? ((p00_val >= 1) ? INT_MAX : 0) \
177  : (((T)-p00_val >= 1) ? INT_MIN : 0)), \
178  p00_file, p00_context, p00_info); \
179  return p00_val; \
180 } \
181 P99_MACRO_END(P00_THROW_CALL_RANGE, T)
182 
183 P00_THROW_CALL_RANGE(_Bool, 0, 1);
184 P00_THROW_CALL_RANGE(char, 0, CHAR_MAX, CHAR_MIN);
185 P00_THROW_CALL_RANGE(schar, 0, SCHAR_MAX, SCHAR_MIN);
186 P00_THROW_CALL_RANGE(short, 0, SHRT_MAX, SHRT_MIN);
187 P00_THROW_CALL_RANGE(int, 0, INT_MAX, INT_MIN);
188 P00_THROW_CALL_RANGE(long, 0, LONG_MAX, LONG_MIN);
189 P00_THROW_CALL_RANGE(llong, 0, LLONG_MAX, LLONG_MIN);
190 P00_THROW_CALL_RANGE(uchar, 0, UCHAR_MAX);
191 P00_THROW_CALL_RANGE(ushort, 0, USHRT_MAX);
192 P00_THROW_CALL_RANGE(unsigned, 0, UINT_MAX);
193 P00_THROW_CALL_RANGE(ulong, 0, ULONG_MAX);
194 P00_THROW_CALL_RANGE(ullong, 0, ULLONG_MAX);
195 # ifdef HUGE_VALF
196 P00_THROW_CALL_RANGE(float, 1, HUGE_VALF, -HUGE_VALF);
197 # endif
198 # ifdef HUGE_VAL
199 P00_THROW_CALL_RANGE(double, 1, HUGE_VAL, -HUGE_VAL);
200 # endif
201 # ifdef HUGE_VALL
202 P00_THROW_CALL_RANGE(ldouble, 1, HUGE_VALL, -HUGE_VALL);
203 # endif
204 
205 #define P00_THROW_CALL_RANGE_CASE(T) ,(T, P99_PASTE2(p00_throw_call_range_, T))
206 
207 #define P00_THROW_CALL_RANGE_(F, CASES, ...) \
208 P99_GENERIC((F)(__VA_ARGS__), P00_ROBUST CASES) \
209  (p00_unwind_top, F(__VA_ARGS__), P99_STRINGIFY(__LINE__), __func__, #F ", range check")
210 
211 #define P99_THROW_CALL_RANGE_ARG_0 permitted
212 #define P99_THROW_CALL_RANGE_ARG_1 permitted
213 #define P99_THROW_CALL_RANGE_ARG_2 permitted
214 #define P99_THROW_CALL_RANGE_ARG_3 permitted
215 #define P99_THROW_CALL_RANGE_ARG_4 permitted
216 
246 P00_DOCUMENT_PERMITTED_ARGUMENT(P99_THROW_CALL_RANGE, 0)
247 P00_DOCUMENT_PERMITTED_ARGUMENT(P99_THROW_CALL_RANGE, 1)
248 P00_DOCUMENT_PERMITTED_ARGUMENT(P99_THROW_CALL_RANGE, 2)
249 P00_DOCUMENT_PERMITTED_ARGUMENT(P99_THROW_CALL_RANGE, 3)
250 P00_DOCUMENT_PERMITTED_ARGUMENT(P99_THROW_CALL_RANGE, 4)
251 #define P99_THROW_CALL_RANGE(F, ...) \
252  P00_THROW_CALL_RANGE_ \
253  (F, \
254  (P99_SER(P00_THROW_CALL_RANGE_CASE, P99_STD_REAL_TYPES)), \
255  __VA_ARGS__)
256 
257 
270 #if defined(noreturn) || defined(_Noreturn)
271 #define P99_THROW_ERRNO p00_throw_errno(p00_unwind_top, EINVAL, P99_STRINGIFY(__LINE__), __func__, "THROW_ERRNO")
272 #else
273 #define P99_THROW_ERRNO \
274 do { \
275  errno_t p00_err = errno; \
276  if (!p00_err) p00_err = EINVAL; \
277  errno = 0; \
278  P99_THROW(p00_err); \
279  } while(0)
280 #endif
281 
283 int p00_throw_call_zero(int p00_err,
284  errno_t p00_def,
285  p00_jmp_buf0 * p00_top,
286  char const* p00_file,
287  char const* p00_context,
288  char const* p00_info) {
289  if (P99_UNLIKELY(p00_err)) p00_throw_errno(p00_top, p00_def, p00_file, p00_context, p00_info);
290  return 0;
291 }
292 
312 #define P99_THROW_CALL_ZERO(F, E, ...) \
313 p00_throw_call_zero(F(__VA_ARGS__), E, p00_unwind_top, P99_STRINGIFY(__LINE__), __func__, #F ", non-zero return")
314 
315 #ifndef NDEBUG
316 
325 #define P99_THROW_ASSERT(E, ...) \
326 (void)p00_throw_call_zero(!(__VA_ARGS__), E, p00_unwind_top, P99_STRINGIFY(__LINE__), __func__, "failed assertion: ``" #__VA_ARGS__ "''")
327 #else
328 #define P99_THROW_ASSERT(...) P99_NOP
329 #endif
330 
339 #define P99_THROW_CALL_NOT_ZERO(F, E, ...) \
340 p00_throw_call_zero(!F(__VA_ARGS__), E, p00_unwind_top, P99_STRINGIFY(__LINE__), __func__, #F ", zero return")
341 
343 int p00_throw_call_thrd(int p00_err,
344  p00_jmp_buf0 * p00_top,
345  char const* p00_file,
346  char const* p00_context,
347  char const* p00_info) {
348  if (P99_UNLIKELY(p00_err != thrd_success))
349  p00_jmp_throw(p00_err, p00_top, p00_file, p00_context, p00_info);
350  return 0;
351 }
352 
374 #define P99_THROW_CALL_THRD(F, ...) \
375 p00_throw_call_thrd(F(__VA_ARGS__), p00_unwind_top, P99_STRINGIFY(__LINE__), __func__, #F ", no thrd_success")
376 
378 int p00_throw_call_neg(int p00_neg,
379  errno_t p00_def,
380  p00_jmp_buf0 * p00_top,
381  char const* p00_file,
382  char const* p00_context,
383  char const* p00_info) {
384  if (P99_UNLIKELY(p00_neg < 0)) p00_throw_errno(p00_top, p00_def, p00_file, p00_context, p00_info);
385  return p00_neg;
386 }
387 
388 #define P00_THROW_CALL_NEG(F, E, ...) \
389 p00_throw_call_neg(F(__VA_ARGS__), E, p00_unwind_top, P99_STRINGIFY(__LINE__), __func__, #F ", neg return")
390 
410 #define P99_THROW_CALL_NEG(F, E, ...) P00_THROW_CALL_NEG(F, E, __VA_ARGS__)
411 
413 int p00_throw_call_negate(int p00_neg,
414  errno_t p00_def,
415  p00_jmp_buf0 * p00_top,
416  char const* p00_file,
417  char const* p00_context,
418  char const* p00_info) {
419  P99_UNUSED(p00_def);
420  if (P99_UNLIKELY(p00_neg < 0)) p00_jmp_throw(-p00_neg, p00_top, p00_file, p00_context, p00_info);
421  return p00_neg;
422 }
423 
424 #define P00_THROW_CALL_NEGATE(F, E, ...) \
425 p00_throw_call_negate(F(__VA_ARGS__), E, p00_unwind_top, P99_STRINGIFY(__LINE__), __func__, #F ", neg return")
426 
451 #define P99_THROW_CALL_NEGATE(F, E, ...) P00_THROW_CALL_NEGATE(F, E, __VA_ARGS__)
452 
454 void* p00_throw_call_voidp(void* p00_p,
455  errno_t p00_def,
456  p00_jmp_buf0 * p00_top,
457  char const* p00_file,
458  char const* p00_context,
459  char const* p00_info) {
460  if (P99_UNLIKELY(!p00_p || (p00_p == (void*)-1))) p00_throw_errno(p00_top, p00_def, p00_file, p00_context, p00_info);
461  return p00_p;
462 }
463 
464 #define P00_THROW_CALL_VOIDP(F, E, ...) \
465 p00_throw_call_voidp(F(__VA_ARGS__), E, p00_unwind_top, P99_STRINGIFY(__LINE__), __func__, #F ", invalid return")
466 
467 
489 #define P99_THROW_CALL_VOIDP(F, E, ...) P00_THROW_CALL_VOIDP(F, E, __VA_ARGS__)
490 
496 P00_UNWIND_DOCUMENT
497 #define P99_RETHROW p00_jmp_throw(p00_code, p00_unwind_top, 0, 0, 0)
498 
499 
586 P00_UNWIND_DOCUMENT
587 #define P99_TRY \
588 P99_UNWIND_PROTECT \
589 /* one phase for the try, one for the finally */ \
590 for (register unsigned p00_pha = 0; p00_pha < 2u; ++p00_pha) \
591  /* Restrict the first phase to the try */ \
592  if (!p00_pha) \
593  P00_BLK_START \
594  P00_BLK_BEFORE(p00_jmp_push(p00_unwind_top)) \
595  do
596 
597 #define P00_FINALLY \
598 while (0); else case 0: \
599 P00_BLK_START \
600 P00_BLK_BEFORE(p00_code = p00_unwind_top[0].p00_code) \
601 P00_BLK_BEFORE(p00_unw = !!p00_code) \
602 /* make sure that this phase is executed at most once */ \
603 P00_BLK_BEFORE(p00_pha = 2u) \
604 /* unwind the lifo to the current point */ \
605 P00_BLK_BEFORE(p00_jmp_skip(p00_unwind_top)) \
606 /* Hide the top most jump buffer, such that throw or rethrow inside \
607  the catch or finally blocks goes to the next level. */ \
608 P00_BLK_DECL(p00_jmp_buf0*, p00_unwind_top, p00_unwind_prev)
609 
610 
632 P00_UNWIND_DOCUMENT
633 #define P99_FINALLY \
634 P00_FINALLY \
635 P00_BLK_AFTER(p00_unw ? P99_RETHROW : P99_NOP)
636 
678 P00_UNWIND_DOCUMENT
679 #define P99_CATCH(...) \
680 P00_FINALLY \
681 P99_IF_EMPTY(__VA_ARGS__)()(P00_BLK_BEFORE(__VA_ARGS__ = p00_code)) \
682 P00_BLK_BEFORE(p00_unw = 0) \
683 P00_BLK_AFTER(p00_code ? (void)((P00_JMP_BUF_FILE = 0), (P00_JMP_BUF_CONTEXT = 0)) : P99_NOP)
684 
690 #endif
P99_LIFO_PUSH
#define P99_LIFO_PUSH(L, EL)
Push element EL into an atomic LIFO L.
Definition: p99_lifo.h:68
P99_LIKELY
#define P99_LIKELY(...)
Mark the conditional expression as being likely.
Definition: p99_compiler.h:1010
noreturn
#define noreturn
Declare a function that doesn't return to the caller.
Definition: p99_compiler.h:842
P99_LIFO_DECLARE
#define P99_LIFO_DECLARE(T)
Definition: p99_lifo.h:45
p99_jmp_report
void p99_jmp_report(errno_t p00_cond)
Report the origin and cause of an error.
Definition: p99_try.h:70
ulong
unsigned long ulong
a ‘one token’ abreviation for unsigned long
Definition: p99_typenames.h:38
schar
signed char schar
a ‘one token’ abreviation for signed char
Definition: p99_typenames.h:45
P99_LIFO_TOP
#define P99_LIFO_TOP(L)
Return a pointer to the top element of an atomic LIFO L.
Definition: p99_lifo.h:57
ldouble
long double ldouble
a ‘one token’ abreviation for long double
Definition: p99_typenames.h:49
EINVAL
P00_CLAUSE2 call to dynamic constraint EINVAL
Definition: p99_constraint.h:195
thrd_success
@ thrd_success
returned by a function to indicate that the requested operation succeeded
Definition: p99_tss.h:118
p99_inline
#define p99_inline
Try to force a function always to be inlined.
Definition: p99_compiler.h:496
P99_UNUSED
#define P99_UNUSED(...)
check if the list of expressions is syntactically valid but don't evaluate it
Definition: p99_enum.h:215
P99_THROW_CALL_RANGE
#define P99_THROW_CALL_RANGE(F,...)
Wrap a function call to F such that it throws an error on failure.
Definition: p99_try.h:249
ullong
unsigned long long ullong
a ‘one token’ abreviation for unsigned long long
Definition: p99_typenames.h:48
p99_lifo.h
P99_DECLARE_THREAD_LOCAL
#define P99_DECLARE_THREAD_LOCAL
P99_POINTER_TYPE
#define P99_POINTER_TYPE(T)
Declare derived pointer types with endings "_ptr" and "_cptr".
Definition: p99_type.h:132
P99_LIFO
#define P99_LIFO(T)
Definition: p99_lifo.h:42
llong
long long llong
a ‘one token’ abreviation for long long
Definition: p99_typenames.h:46
P99_LIFO_POP
#define P99_LIFO_POP(L)
Pop the top element from an atomic LIFO L.
Definition: p99_lifo.h:120
ushort
unsigned short ushort
a ‘one token’ abreviation for unsigned short
Definition: p99_typenames.h:42
P99_UNLIKELY
#define P99_UNLIKELY(...)
Mark the conditional expression as being unlikely.
Definition: p99_compiler.h:994
p99_constraint.h
errno
errno
Definition: p99_constraint.h:199
errno_t
int errno_t
Definition: p99_type.h:38
constraint_handler_t
struct atomic_constraint_handler_t constraint_handler_t
Definition: p99_constraint.h:331
uchar
unsigned char uchar
a ‘one token’ abreviation for unsigned char
Definition: p99_typenames.h:44