cexcept.h 10 KB

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