Visualization Library 2.0.0-b5

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

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