openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
cexcept.h
Go to the documentation of this file.
1 /*===
2 cexcept.h 2.0.0 (2001-Jul-12-Thu)
3 Adam M. Costello <amc@cs.berkeley.edu>
4 
5 An interface for exception-handling in ANSI C (C89 and subsequent ISO
6 standards), developed jointly with Cosmin Truta <cosmin@cs.toronto.edu>.
7 
8  Copyright (c) 2001 Adam M. Costello and Cosmin Truta. Everyone
9  is hereby granted permission to do whatever they like with this
10  file, provided that if they modify it they take reasonable steps to
11  avoid confusing or misleading people about the authors, version,
12  and terms of use of the derived file. The copyright holders make
13  no guarantees regarding this file, and are not responsible for any
14  damage resulting from its use.
15 
16 Only user-defined exceptions are supported, not "real" exceptions like
17 division by zero or memory segmentation violations.
18 
19 If this interface is used by multiple .c files, they shouldn't include
20 this header file directly. Instead, create a wrapper header file that
21 includes this header file and then invokes the define_exception_type
22 macro (see below), and let your .c files include that header file.
23 
24 The interface consists of one type, one well-known name, and six macros.
25 
26 
27 define_exception_type(type_name);
28 
29  This macro is used like an external declaration. It specifies
30  the type of object that gets copied from the exception thrower to
31  the exception catcher. The type_name can be any type that can be
32  assigned to, that is, a non-constant arithmetic type, struct, union,
33  or pointer. Examples:
34 
35  define_exception_type(int);
36 
37  enum exception { out_of_memory, bad_arguments, disk_full };
38  define_exception_type(enum exception);
39 
40  struct exception { int code; const char *msg; };
41  define_exception_type(struct exception);
42 
43  Because throwing an exception causes the object to be copied (not
44  just once, but twice), programmers may wish to consider size when
45  choosing the exception type.
46 
47 
48 struct exception_context;
49 
50  This type may be used after the define_exception_type() macro has
51  been invoked. A struct exception_context must be known to both
52  the thrower and the catcher. It is expected that there be one
53  context for each thread that uses exceptions. It would certainly
54  be dangerous for multiple threads to access the same context.
55  One thread can use multiple contexts, but that is likely to be
56  confusing and not typically useful. The application can allocate
57  this structure in any way it pleases--automatic, static, or dynamic.
58  The application programmer should pretend not to know the structure
59  members, which are subject to change.
60 
61 
62 struct exception_context *the_exception_context;
63 
64  The Try/Catch and Throw statements (described below) implicitly
65  refer to a context, using the name the_exception_context. It is
66  the application's responsibility to make sure that this name yields
67  the address of a mutable (non-constant) struct exception_context
68  wherever those statements are used. Subject to that constraint, the
69  application may declare a variable of this name anywhere it likes
70  (inside a function, in a parameter list, or externally), and may
71  use whatever storage class specifiers (static, extern, etc) or type
72  qualifiers (const, volatile, etc) it likes. Examples:
73 
74  static struct exception_context
75  * const the_exception_context = &foo;
76 
77  { struct exception_context *the_exception_context = bar; ... }
78 
79  int blah(struct exception_context *the_exception_context, ...);
80 
81  extern struct exception_context the_exception_context[1];
82 
83  The last example illustrates a trick that avoids creating a pointer
84  object separate from the structure object.
85 
86  The name could even be a macro, for example:
87 
88  struct exception_context ec_array[numthreads];
89  #define the_exception_context (ec_array + thread_id)
90 
91  Be aware that the_exception_context is used several times by the
92  Try/Catch/Throw macros, so it shouldn't be expensive or have side
93  effects. The expansion must be a drop-in replacement for an
94  identifier, so it's safest to put parentheses around it.
95 
96 
97 void init_exception_context(struct exception_context *ec);
98 
99  For context structures allocated statically (by an external
100  definition or using the "static" keyword), the implicit
101  initialization to all zeros is sufficient, but contexts allocated
102  by other means must be initialized using this macro before they
103  are used by a Try/Catch statement. It does no harm to initialize
104  a context more than once (by using this macro on a statically
105  allocated context, or using this macro twice on the same context),
106  but a context must not be re-initialized after it has been used by a
107  Try/Catch statement.
108 
109 
110 Try statement
111 Catch (expression) statement
112 
113  The Try/Catch/Throw macros are capitalized in order to avoid
114  confusion with the C++ keywords, which have subtly different
115  semantics.
116 
117  A Try/Catch statement has a syntax similar to an if/else statement,
118  except that the parenthesized expression goes after the second
119  keyword rather than the first. As with if/else, there are two
120  clauses, each of which may be a simple statement ending with a
121  semicolon or a brace-enclosed compound statement. But whereas
122  the else clause is optional, the Catch clause is required. The
123  expression must be a modifiable lvalue (something capable of being
124  assigned to) of the same type (disregarding type qualifiers) that
125  was passed to define_exception_type().
126 
127  If a Throw that uses the same exception context as the Try/Catch is
128  executed within the Try clause (typically within a function called
129  by the Try clause), and the exception is not caught by a nested
130  Try/Catch statement, then a copy of the exception will be assigned
131  to the expression, and control will jump to the Catch clause. If no
132  such Throw is executed, then the assignment is not performed, and
133  the Catch clause is not executed.
134 
135  The expression is not evaluated unless and until the exception is
136  caught, which is significant if it has side effects, for example:
137 
138  Try foo();
139  Catch (p[++i].e) { ... }
140 
141  IMPORTANT: Jumping into or out of a Try clause (for example via
142  return, break, continue, goto, longjmp) is forbidden--the compiler
143  will not complain, but bad things will happen at run-time. Jumping
144  into or out of a Catch clause is okay, and so is jumping around
145  inside a Try clause. In many cases where one is tempted to return
146  from a Try clause, it will suffice to use Throw, and then return
147  from the Catch clause. Another option is to set a flag variable and
148  use goto to jump to the end of the Try clause, then check the flag
149  after the Try/Catch statement.
150 
151  IMPORTANT: The values of any non-volatile automatic variables
152  changed within the Try clause are undefined after an exception is
153  caught. Therefore, variables modified inside the Try block whose
154  values are needed later outside the Try block must either use static
155  storage or be declared with the "volatile" type qualifier.
156 
157 
158 Throw expression;
159 
160  A Throw statement is very much like a return statement, except that
161  the expression is required. Whereas return jumps back to the place
162  where the current function was called, Throw jumps back to the Catch
163  clause of the innermost enclosing Try clause. The expression must
164  be compatible with the type passed to define_exception_type(). The
165  exception must be caught, otherwise the program may crash.
166 
167  Slight limitation: If the expression is a comma-expression it must
168  be enclosed in parentheses.
169 
170 
171 Try statement
172 Catch_anonymous statement
173 
174  When the value of the exception is not needed, a Try/Catch statement
175  can use Catch_anonymous instead of Catch (expression).
176 
177 
178 Everything below this point is for the benefit of the compiler. The
179 application programmer should pretend not to know any of it, because it
180 is subject to change.
181 
182 ===*/
183 
184 
185 #ifndef CEXCEPT_H
186 #define CEXCEPT_H
187 
188 #ifdef __cplusplus
189 
190 #define Throw throw
191 #define Catch( A ) catch( struct exception_type A )
192 
193 #else
194 
195 #include <setjmp.h>
196 
197 #define define_exception_type(etype) \
198  struct exception_context { \
199  jmp_buf *penv; \
200  int caught; \
201  volatile struct { etype etmp; } v; \
202  }
203 
204 /* etmp must be volatile because the application might use automatic */
205 /* storage for the_exception_context, and etmp is modified between */
206 /* the calls to setjmp() and longjmp(). A wrapper struct is used to */
207 /* avoid warnings about a duplicate volatile qualifier in case etype */
208 /* already includes it. */
209 
210 #define init_exception_context(ec) ((void)((ec)->penv = 0))
211 
212 #define Try \
213  { \
214  jmp_buf *exception__prev, exception__env; \
215  exception__prev = the_exception_context->penv; \
216  the_exception_context->penv = &exception__env; \
217  if (setjmp(exception__env) == 0) { \
218  if (&exception__prev)
219 
220 #define exception__catch(action) \
221  else { } \
222  the_exception_context->caught = 0; \
223  } \
224  else { \
225  the_exception_context->caught = 1; \
226  } \
227  the_exception_context->penv = exception__prev; \
228  } \
229  if (!the_exception_context->caught || action) { } \
230  else
231 
232 #define Catch(e) exception__catch(((e) = the_exception_context->v.etmp, 0))
233 #define Catch_anonymous exception__catch(0)
234 
235 /* Try ends with if(), and Catch begins and ends with else. This */
236 /* ensures that the Try/Catch syntax is really the same as the */
237 /* if/else syntax. */
238 /* */
239 /* We use &exception__prev instead of 1 to appease compilers that */
240 /* warn about constant expressions inside if(). Most compilers */
241 /* should still recognize that &exception__prev is never zero and */
242 /* avoid generating test code. */
243 
244 #define Throw \
245  for (;; longjmp(*the_exception_context->penv, 1)) \
246  the_exception_context->v.etmp =
247 
248 #endif // __cplusplus
249 #endif /* CEXCEPT_H */