Guest User

Untitled

a guest
Nov 19th, 2018
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.95 KB | None | 0 0
  1. /**
  2. * Automatic scoping and destructor functionality a la scope() in D
  3. * ExtendedC
  4. *
  5. * by Justin Spahr-Summers
  6. *
  7. * Released 9. Nov 2010 into the public domain.
  8. */
  9.  
  10. #ifndef EXTC_HEADER_SCOPE_H
  11. #define EXTC_HEADER_SCOPE_H
  12.  
  13. #ifdef HAVE_CONFIG_H
  14. #include "config.h"
  15. #endif
  16.  
  17. #include <limits.h>
  18. #include <stdbool.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include "metamacros.h"
  22.  
  23. /**
  24. * Expands to scope_KEYWORD. Allows usage of the scope macros more like the D
  25. * language construct.
  26. *
  27. * KEYWORD must be one of the following:
  28. *
  29. * new
  30. * creates a new scope
  31. * (see scope_new)
  32. *
  33. * exit
  34. * performs a block when the current scope is exited
  35. * (see scope_exit)
  36. */
  37. #define scope(KEYWORD) \
  38. scope_ ## KEYWORD
  39.  
  40. /**
  41. * Creates a new scope that performs automatic cleanup when exited. Works like
  42. * a statement, in that it takes a block of code that becomes the scope.
  43. *
  44. * Some notes for usage:
  45. * - Variables that will be used in cleanup blocks must be declared OUTSIDE
  46. * of the scope. They will retain their assigned values.
  47. * - Scopes must never contain declarations for variable-length arrays
  48. * because of the underlying 'goto' and 'switch' mechanics.
  49. * - Only one 'scope_new' can appear in any given function. This
  50. * restriction may be removed in future versions.
  51. * - It is illegal to 'return' or 'goto' out of a scope. 'sreturn' provides
  52. * the functionality of the former.
  53. */
  54. #define scope_new \
  55. /* set up the state variable that indicates what the scope is doing */ \
  56. for (enum scope_cleanup_t scope_cleanup_state_ = SCOPE_EXECUTING; \
  57. scope_cleanup_state_ != SCOPE_EXITING; \
  58. scope_cleanup_state_ = SCOPE_EXITING) \
  59. /* set up an array for the line numbers of scope(exit) statements */ \
  60. /* jmplines is actually a stack so that cleanup is done in reverse */ \
  61. for (unsigned int scope_jmplines_[SCOPE_DESTRUCTOR_LIMIT]; \
  62. scope_cleanup_state_ != SCOPE_EXITING; \
  63. scope_cleanup_state_ = SCOPE_EXITING) \
  64. /* initialize the first "line" with 0 so it always enters default: */ \
  65. for (scope_jmplines_[0] = 0; \
  66. scope_cleanup_state_ != SCOPE_EXITING; \
  67. scope_cleanup_state_ = SCOPE_EXITING) \
  68. /* set up variables for positioning in the lines array */ \
  69. for (unsigned short scope_cleanup_count_ = 0, scope_cleanup_index_ = 0; \
  70. scope_cleanup_state_ != SCOPE_EXITING; \
  71. scope_cleanup_state_ = SCOPE_EXITING) \
  72. \
  73. /* label this spot so exit blocks can jump out, then in to the next one */ \
  74. /* unfortunately, this does prevent multiple scopes from existing */ \
  75. scope_loop_: \
  76. /* this loop does two things:
  77. - if the scope has yet to execute, it executes it once
  78. - if the scope has yet to clean up, it jumps to each cleanup block
  79. */ \
  80. for (; \
  81. /* loop while executing or there are still cleanup blocks to hit */ \
  82. scope_cleanup_state_ == SCOPE_EXECUTING || ( \
  83. /* include index 0 if it's the location for an sreturn statement */ \
  84. (scope_cleanup_index_ > 0 || scope_jmplines_[0] != 0) && \
  85. scope_cleanup_index_ < SCOPE_DESTRUCTOR_LIMIT \
  86. ); \
  87. /* after one full execution, change state to start cleaning up */ \
  88. scope_cleanup_state_ = SCOPE_CLEANING_UP, \
  89. scope_cleanup_index_ = scope_cleanup_count_) \
  90. /* jump to the appropriate exit block, or default: if not cleaning */ \
  91. switch (scope_jmplines_[scope_cleanup_index_]) \
  92. default: \
  93. /* normal execution flow begins here */ \
  94. /* 'break' or 'continue' will exit the switch and begin cleanup */
  95.  
  96. /**
  97. * Defines some cleanup code to be executed when the current scope is left.
  98. * Works just like a statement in that it can take one line, or multiple lines
  99. * surrounded by braces.
  100. *
  101. * Multiple cleanup blocks are executed in reverse lexical order.
  102. *
  103. * Some notes for usage:
  104. * - Variables that will be used in cleanup blocks must be declared OUTSIDE
  105. * of the scope. They will retain their assigned values.
  106. * - It is illegal to 'return' or 'goto' out of a cleanup block.
  107. * - Currently, exceptions cause cleanup blocks to be skipped. This may be
  108. * fixed in a future version.
  109. */
  110. #define scope_exit \
  111. /* this if statement will only ever get hit during normal flow */ \
  112. if (scope_cleanup_count_ + 1 >= SCOPE_DESTRUCTOR_LIMIT) { \
  113. fprintf(stderr, "*** Too many scope(exit) statements, skipping %s:%lu", \
  114. __FILE__, (unsigned long)__LINE__); \
  115. } else if ( \
  116. /* mark this spot for jumping during cleanup */ \
  117. (scope_jmplines_[++scope_cleanup_index_] = __LINE__) && \
  118. /* increment the number of cleanup blocks */ \
  119. ++scope_cleanup_count_ && \
  120. /* and, of course, don't actually enter this statement */ \
  121. 0 \
  122. ) \
  123. /* execution will jump straight here during cleanup */ \
  124. case __LINE__: \
  125. /* meaningless outer loop to allow the use of 'break' */ \
  126. for (bool scope_done_once_ = false;;scope_done_once_ = true) \
  127. /* executes this block of code only once */ \
  128. for (;;scope_done_once_ = true) \
  129. /* if finished... */ \
  130. if (scope_done_once_) { \
  131. /* mark this cleanup block as being finished */ \
  132. --scope_cleanup_index_; \
  133. /* return to the loop so the next one will be done */ \
  134. goto scope_loop_; \
  135. } else \
  136. /* cleanup code begins here */
  137.  
  138. /**
  139. * Exits the current scope, runs any cleanup code, and returns the given value
  140. * (if present).
  141. *
  142. * Essentially, this is invoked identically to built-in 'return', but should
  143. * always be used instead of 'return' inside a scope, and cannot be used outside
  144. * of one.
  145. *
  146. * sreturn is legal within scope(exit) blocks. The final sreturn statement that
  147. * is executed provides the value that is actually returned to the caller.
  148. *
  149. * Although a given sreturn statement may always be executed, the compiler may
  150. * still issue warnings about your non-void function not returning a value. To
  151. * suppress these warnings, you can use 'return' with a bogus value at the very
  152. * end of your function. Many compilers also shut up if you use assert(0); at
  153. * the very end.
  154. */
  155. #define sreturn \
  156. /* if there are cleanup blocks... */ \
  157. if (scope_cleanup_count_) { \
  158. /* we need to mark this spot to return to after all the cleanup */ \
  159. /* subvert the stack and insert this line in the logically last spot */ \
  160. scope_jmplines_[0] = __LINE__; \
  161. \
  162. /* if already cleaning up (meaning this is within a cleanup block)... */ \
  163. if (scope_cleanup_state_ == SCOPE_CLEANING_UP) { \
  164. /* jump to the next cleanup block now that this spot is marked */ \
  165. --scope_cleanup_index_; \
  166. } else { \
  167. /* otherwise, start all the normal cleanup logic */ \
  168. scope_cleanup_state_ = SCOPE_CLEANING_UP; \
  169. scope_cleanup_index_ = scope_cleanup_count_; \
  170. } \
  171. \
  172. goto scope_loop_; \
  173. /* otherwise, actually exit the function */ \
  174. } else \
  175. /* mark this point for jumping if we have cleanup */ \
  176. case __LINE__: \
  177. /* pass on the user's return value */ \
  178. return
  179.  
  180. // IMPLEMENTATION DETAILS FOLLOW!
  181. // Do not write code that depends on anything below this line.
  182. enum scope_cleanup_t {
  183. SCOPE_EXECUTING,
  184. SCOPE_CLEANING_UP,
  185. SCOPE_EXITING
  186. };
  187.  
  188. // the maximum number of scope(exit) statements allowed
  189. #define SCOPE_DESTRUCTOR_LIMIT 128
  190.  
  191. #endif
Add Comment
Please, Sign In to add comment