1 | <html> |
---|
2 | <head> |
---|
3 | <title>libsm : Exception Handling</title> |
---|
4 | </head> |
---|
5 | <body> |
---|
6 | |
---|
7 | <a href="index.html">Back to libsm overview</a> |
---|
8 | |
---|
9 | <center> |
---|
10 | <h1> libsm : Exception Handling </h1> |
---|
11 | <br> $Id: exc.html,v 1.1.1.1 2003-04-08 15:11:54 zacheiss Exp $ |
---|
12 | </center> |
---|
13 | |
---|
14 | <h2> Introduction </h2> |
---|
15 | |
---|
16 | The exception handling package provides the facilities that |
---|
17 | functions in libsm use to report errors. |
---|
18 | Here are the basic concepts: |
---|
19 | |
---|
20 | <ol> |
---|
21 | <li> |
---|
22 | When a function detects an exceptional condition at the library level, |
---|
23 | it does not print an error message, or call syslog, or |
---|
24 | exit the program. Instead, it reports the error back to its |
---|
25 | caller, and lets the caller decide what to do. |
---|
26 | This improves modularity, because error handling is separated |
---|
27 | from error reporting. |
---|
28 | <p> |
---|
29 | <li> |
---|
30 | Errors are not represented by a single integer error code, |
---|
31 | because that you can't represent everything that an error handler |
---|
32 | might need to know about an error by a single integer. |
---|
33 | Instead, errors are represented by exception objects. |
---|
34 | An exception object contains an exception code and an array |
---|
35 | of zero or more exception arguments. |
---|
36 | The exception code is a string that specifies what kind of exception |
---|
37 | this is, and the arguments may be integers, strings or exception objects. |
---|
38 | <p> |
---|
39 | <li> |
---|
40 | Errors are not reported using a special return value, |
---|
41 | because if you religiously check for error returns from every |
---|
42 | function call that could fail, then most of your code ends up being |
---|
43 | error handling code. Errors are reported by raising an exception. |
---|
44 | When an exception is raised, we unwind the call stack |
---|
45 | until we find an exception handler. If the exception is |
---|
46 | not handled, then we print the exception on stderr and |
---|
47 | exit the program. |
---|
48 | </ol> |
---|
49 | |
---|
50 | <h2> Synopsis </h2> |
---|
51 | |
---|
52 | <pre> |
---|
53 | #include <sm/exc.h> |
---|
54 | |
---|
55 | typedef struct sm_exc_type SM_EXC_TYPE_T; |
---|
56 | typedef struct sm_exc SM_EXC_T; |
---|
57 | typedef union sm_val SM_VAL_T; |
---|
58 | |
---|
59 | /* |
---|
60 | ** Exception types |
---|
61 | */ |
---|
62 | |
---|
63 | extern const char SmExcTypeMagic[]; |
---|
64 | |
---|
65 | struct sm_exc_type |
---|
66 | { |
---|
67 | const char *sm_magic; |
---|
68 | const char *etype_category; |
---|
69 | const char *etype_argformat; |
---|
70 | void (*etype_print)(SM_EXC_T *exc, SM_FILE_T *stream); |
---|
71 | const char *etype_printcontext; |
---|
72 | }; |
---|
73 | |
---|
74 | extern const SM_EXC_TYPE_T SmEtypeOs; |
---|
75 | extern const SM_EXC_TYPE_T SmEtypeErr; |
---|
76 | |
---|
77 | void |
---|
78 | sm_etype_printf( |
---|
79 | SM_EXC_T *exc, |
---|
80 | SM_FILE_T *stream); |
---|
81 | |
---|
82 | /* |
---|
83 | ** Exception objects |
---|
84 | */ |
---|
85 | |
---|
86 | extern const char SmExcMagic[]; |
---|
87 | |
---|
88 | union sm_val |
---|
89 | { |
---|
90 | int v_int; |
---|
91 | long v_long; |
---|
92 | char *v_str; |
---|
93 | SM_EXC_T *v_exc; |
---|
94 | }; |
---|
95 | |
---|
96 | struct sm_exc |
---|
97 | { |
---|
98 | const char *sm_magic; |
---|
99 | size_t exc_refcount; |
---|
100 | const SM_EXC_TYPE_T *exc_type; |
---|
101 | SM_VAL_T *exc_argv; |
---|
102 | }; |
---|
103 | |
---|
104 | SM_EXC_T * |
---|
105 | sm_exc_new_x( |
---|
106 | const SM_EXC_TYPE_T *type, |
---|
107 | ...); |
---|
108 | |
---|
109 | SM_EXC_T * |
---|
110 | sm_exc_addref( |
---|
111 | SM_EXC_T *exc); |
---|
112 | |
---|
113 | void |
---|
114 | sm_exc_free( |
---|
115 | SM_EXC_T *exc); |
---|
116 | |
---|
117 | bool |
---|
118 | sm_exc_match( |
---|
119 | SM_EXC_T *exc, |
---|
120 | const char *pattern); |
---|
121 | |
---|
122 | void |
---|
123 | sm_exc_print( |
---|
124 | SM_EXC_T *exc, |
---|
125 | SM_FILE_T *stream); |
---|
126 | |
---|
127 | void |
---|
128 | sm_exc_write( |
---|
129 | SM_EXC_T *exc, |
---|
130 | SM_FILE_T *stream); |
---|
131 | |
---|
132 | void |
---|
133 | sm_exc_raise_x( |
---|
134 | SM_EXC_T *exc); |
---|
135 | |
---|
136 | void |
---|
137 | sm_exc_raisenew_x( |
---|
138 | const SM_EXC_TYPE_T *type, |
---|
139 | ...); |
---|
140 | |
---|
141 | /* |
---|
142 | ** Ensure that cleanup code is executed, |
---|
143 | ** and/or handle an exception. |
---|
144 | */ |
---|
145 | SM_TRY |
---|
146 | Block of code that may raise an exception. |
---|
147 | SM_FINALLY |
---|
148 | Cleanup code that may raise an exception. |
---|
149 | This clause is guaranteed to be executed even if an exception is |
---|
150 | raised by the SM_TRY clause or by an earlier SM_FINALLY clause. |
---|
151 | You may have 0 or more SM_FINALLY clauses. |
---|
152 | SM_EXCEPT(exc, pattern) |
---|
153 | Exception handling code, triggered by an exception |
---|
154 | whose category matches 'pattern'. |
---|
155 | You may have 0 or more SM_EXCEPT clauses. |
---|
156 | SM_END_TRY |
---|
157 | </pre> |
---|
158 | |
---|
159 | <h2> Overview </h2> |
---|
160 | |
---|
161 | An exception is an object which represents an exceptional condition, |
---|
162 | which might be an error condition like "out of memory", or might be |
---|
163 | a condition like "end of file". |
---|
164 | <p> |
---|
165 | Functions in libsm report errors and other unusual conditions by |
---|
166 | raising an exception, rather than by returning an error code or |
---|
167 | setting a global variable such as errno. If a libsm function is |
---|
168 | capable of raising an exception, its name ends in "_x". |
---|
169 | (We do not raise an exception when a bug is detected in the |
---|
170 | program; instead, we terminate the program using <tt>sm_abort</tt>. |
---|
171 | See <a href="assert.html">the assertion package</a> |
---|
172 | for details.) |
---|
173 | <p> |
---|
174 | When you are using the libsm exception handling package, |
---|
175 | you are using a new programming paradigm. |
---|
176 | You will need to abandon some of the programming idioms |
---|
177 | you are accustomed to, and switch to new idioms. |
---|
178 | Here is an overview of some of these idioms. |
---|
179 | <ol> |
---|
180 | <li> |
---|
181 | When a function is unable to complete its task because |
---|
182 | of an exceptional condition, it reports this condition |
---|
183 | by raising an exception. |
---|
184 | <p> |
---|
185 | Here is an example of how to construct an exception object |
---|
186 | and raise an exception. |
---|
187 | In this example, we convert a Unix system error into an exception. |
---|
188 | <blockquote><pre> |
---|
189 | fd = open(path, O_RDONLY); |
---|
190 | if (fd == -1) |
---|
191 | sm_exc_raise_x(sm_exc_new_x(&SmEtypeOs, errno, "open", "%s", path)); |
---|
192 | </pre></blockquote> |
---|
193 | |
---|
194 | Because the idiom <tt>sm_exc_raise_x(sm_exc_new_x(...))</tt> |
---|
195 | is so common, it can be abbreviated as <tt>sm_exc_raisenew_x(...)</tt>. |
---|
196 | <p> |
---|
197 | <li> |
---|
198 | When you detect an error at the application level, |
---|
199 | you don't call a function like BSD's <tt>errx</tt>, |
---|
200 | which prints an error message on stderr and exits the program. |
---|
201 | Instead, you raise an exception. |
---|
202 | This causes cleanup code in surrounding exception handlers |
---|
203 | to be run before the program exits. |
---|
204 | For example, instead of this: |
---|
205 | <blockquote><pre> |
---|
206 | errx(1, "%s:%d: syntax error", filename, lineno); |
---|
207 | </pre></blockquote> |
---|
208 | |
---|
209 | use this: |
---|
210 | |
---|
211 | <blockquote><pre> |
---|
212 | sm_exc_raisenew_x(&SmEtypeErr, "%s:%d: syntax error", filename, lineno); |
---|
213 | </pre></blockquote> |
---|
214 | |
---|
215 | The latter code raises an exception, unwinding the call stack |
---|
216 | and executing cleanup code. |
---|
217 | If the exception is not handled, then the exception is printed |
---|
218 | to stderr and the program exits. |
---|
219 | The end result is substantially the same as a call to <tt>errx</tt>. |
---|
220 | <p> |
---|
221 | <li> |
---|
222 | The SM_TRY ... SM_FINALLY ... control structure |
---|
223 | ensures that cleanup code is executed and resources are released |
---|
224 | in the presence of exceptions. |
---|
225 | <p> |
---|
226 | For example, suppose that you have written the following code: |
---|
227 | |
---|
228 | <blockquote><pre> |
---|
229 | rpool = sm_rpool_new_x(&SmRpoolRoot, 0); |
---|
230 | ... some code ... |
---|
231 | sm_rpool_free_x(rpool); |
---|
232 | </pre></blockquote> |
---|
233 | |
---|
234 | If any of the functions called within "... some code ..." have |
---|
235 | names ending in _x, then it is possible that an exception will be |
---|
236 | raised, and if that happens, then "rpool" will not be freed. |
---|
237 | And that's a bug. To fix this bug, change your code so it looks |
---|
238 | like this: |
---|
239 | |
---|
240 | <blockquote><pre> |
---|
241 | rpool = sm_rpool_new_x(&SmRpoolRoot, 0); |
---|
242 | SM_TRY |
---|
243 | ... some code that can raise an exception ... |
---|
244 | SM_FINALLY |
---|
245 | sm_rpool_free_x(rpool); |
---|
246 | SM_END_TRY |
---|
247 | </pre></blockquote> |
---|
248 | |
---|
249 | <li> |
---|
250 | The SM_TRY ... SM_EXCEPT ... control structure handles an exception. |
---|
251 | Unhandled exceptions terminate the program. |
---|
252 | For example, here is a simple exception handler |
---|
253 | that traps all exceptions, and prints the exceptions: |
---|
254 | |
---|
255 | <blockquote><pre> |
---|
256 | SM_TRY |
---|
257 | /* code that can raise an exception */ |
---|
258 | ... |
---|
259 | SM_EXCEPT(exc, "*") |
---|
260 | /* catch all exceptions */ |
---|
261 | sm_exc_print(exc, stderr); |
---|
262 | SM_END_TRY |
---|
263 | </pre></blockquote> |
---|
264 | |
---|
265 | Exceptions are reference counted. The SM_END_TRY macro contains a |
---|
266 | call to sm_exc_free, so you don't normally need to worry about freeing |
---|
267 | an exception after handling it. In the rare case that you want an |
---|
268 | exception to outlive an exception handler, then you increment its |
---|
269 | reference count by calling sm_exc_addref. |
---|
270 | <p> |
---|
271 | <li> |
---|
272 | The second argument of the SM_EXCEPT macro is a glob pattern |
---|
273 | which specifies the types of exceptions that are to be handled. |
---|
274 | For example, you might want to handle an end-of-file exception |
---|
275 | differently from other exceptions. |
---|
276 | Here's how you do that: |
---|
277 | |
---|
278 | <blockquote><pre> |
---|
279 | SM_TRY |
---|
280 | /* code that might raise end-of-file, or some other exception */ |
---|
281 | ... |
---|
282 | SM_EXCEPT(exc, "E:sm.eof") |
---|
283 | /* what to do if end-of-file is encountered */ |
---|
284 | ... |
---|
285 | SM_EXCEPT(exc, "*") |
---|
286 | /* what to do if some other exception is raised */ |
---|
287 | ... |
---|
288 | SM_END_TRY |
---|
289 | </pre></blockquote> |
---|
290 | </ol> |
---|
291 | |
---|
292 | <h2> Exception Values </h2> |
---|
293 | |
---|
294 | In traditional C code, errors are usually denoted by a single integer, |
---|
295 | such as errno. In practice, errno does not carry enough information |
---|
296 | to describe everything that an error handler might want to know about |
---|
297 | an error. And the scheme is not very extensible: if several different |
---|
298 | packages want to add additional error codes, it is hard to avoid |
---|
299 | collisions. |
---|
300 | |
---|
301 | <p> |
---|
302 | In libsm, an exceptional condition is described |
---|
303 | by an object of type SM_EXC_T. |
---|
304 | An exception object is created by specifying an exception type |
---|
305 | and a list of exception arguments. |
---|
306 | |
---|
307 | <p> |
---|
308 | The exception arguments are an array of zero or more values. |
---|
309 | The values may be a mixture of ints, longs, strings, and exceptions. |
---|
310 | In the SM_EXC_T structure, the argument vector is represented |
---|
311 | by <tt>SM_VAL_T *exc_argv</tt>, where <tt>SM_VAL_T</tt> |
---|
312 | is a union of the possible argument types. |
---|
313 | The number and types of exception arguments is determined by |
---|
314 | the exception type. |
---|
315 | |
---|
316 | <p> |
---|
317 | An exception type is a statically initialized const object |
---|
318 | of type SM_EXC_TYPE_T, which has the following members: |
---|
319 | |
---|
320 | <dl> |
---|
321 | <dt> |
---|
322 | <tt> const char *sm_magic </tt> |
---|
323 | <dd> |
---|
324 | A pointer to <tt>SmExcTypeMagic</tt>. |
---|
325 | <p> |
---|
326 | <dt> |
---|
327 | <tt> const char *etype_category </tt> |
---|
328 | <dd> |
---|
329 | This is a string of the form |
---|
330 | <tt>"</tt><i>class</i><tt>:</tt><i>name</i><tt>"</tt>. |
---|
331 | <p> |
---|
332 | The <i>class</i> is used to assign the exception type to |
---|
333 | one of a number of broad categories of exceptions on which an |
---|
334 | exception handler might want to discriminate. |
---|
335 | I suspect that what we want is a hierarchical taxonomy, |
---|
336 | but I don't have a full design for this yet. |
---|
337 | For now, I am recommending the following classes: |
---|
338 | <dl> |
---|
339 | <dt><tt>"F"</tt> |
---|
340 | <dd>A fatal error has occurred. |
---|
341 | This is an error that prevents the application |
---|
342 | from making any further progress, so the only |
---|
343 | recourse is to raise an exception, execute cleanup code |
---|
344 | as the stack is unwound, then exit the application. |
---|
345 | The out-of-memory exception raised by sm_malloc_x |
---|
346 | has category "F:sm.heap" because sendmail commits suicide |
---|
347 | (after logging the error and cleaning up) when it runs out |
---|
348 | of memory. |
---|
349 | |
---|
350 | <dt><tt>"E"</tt> |
---|
351 | <dd>The function could not complete its task because an error occurred. |
---|
352 | (It might be useful to define subclasses of this category, |
---|
353 | in which case our taxonony becomes a tree, and 'F' becomes |
---|
354 | a subclass of 'E'.) |
---|
355 | |
---|
356 | <dt><tt>"J"</tt> |
---|
357 | <dd>This exception is being raised in order to effect a |
---|
358 | non-local jump. No error has occurred; we are just |
---|
359 | performing the non-local equivalent of a <tt>continue</tt>, |
---|
360 | <tt>break</tt> or <tt>return</tt>. |
---|
361 | |
---|
362 | <dt><tt>"S"</tt> |
---|
363 | <dd>The function was interrupted by a signal. |
---|
364 | Signals are not errors because they occur asynchronously, |
---|
365 | and they are semantically unrelated to the function that |
---|
366 | happens to be executing when the signal arrives. |
---|
367 | Note that it is extremely dangerous to raise an exception |
---|
368 | from a signal handler. For example, if you are in the middle |
---|
369 | of a call to malloc, you might corrupt the heap. |
---|
370 | </dl> |
---|
371 | Eric's libsm paper defines <tt>"W"</tt>, <tt>"D"</tt> and <tt>"I"</tt> |
---|
372 | for Warning, Debug and Informational: |
---|
373 | I suspect these categories only make sense in the context of |
---|
374 | Eric's 1985 exception handling system which allowed you to |
---|
375 | raise conditions without terminating the calling function. |
---|
376 | <p> |
---|
377 | The <i>name</i> uniquely identifies the exception type. |
---|
378 | I recommend a string of the form |
---|
379 | <i>library</i><tt>.</tt><i>package</i><tt>.</tt><i>detail</i>. |
---|
380 | <p> |
---|
381 | <dt> |
---|
382 | <tt> const char *etype_argformat </tt> |
---|
383 | <dd> |
---|
384 | This is an array of single character codes. |
---|
385 | Each code indicates the type of one of the exception arguments. |
---|
386 | <tt>sm_exc_new_x</tt> uses this string to decode its variable |
---|
387 | argument list into an exception argument vector. |
---|
388 | The following type codes are supported: |
---|
389 | <dl> |
---|
390 | <dt><tt>i</tt> |
---|
391 | <dd> |
---|
392 | The exception argument has type <tt>int</tt>. |
---|
393 | <dt><tt>l</tt> |
---|
394 | <dd> |
---|
395 | The exception argument has type <tt>long</tt>. |
---|
396 | <dt><tt>e</tt> |
---|
397 | <dd> |
---|
398 | The exception argument has type <tt>SM_EXC_T*</tt>. |
---|
399 | The value may either be <tt>NULL</tt> or a pointer |
---|
400 | to an exception. The pointer value is simply copied |
---|
401 | into the exception argument vector. |
---|
402 | <dt><tt>s</tt> |
---|
403 | <dd> |
---|
404 | The exception argument has type <tt>char*</tt>. |
---|
405 | The value may either be <tt>NULL</tt> or a pointer |
---|
406 | to a character string. In the latter case, |
---|
407 | <tt>sm_exc_new_x</tt> will make a copy of the string. |
---|
408 | <dt><tt>r</tt> |
---|
409 | <dd> |
---|
410 | The exception argument has type <tt>char*</tt>. |
---|
411 | <tt>sm_exc_new_x</tt> will read a printf-style |
---|
412 | format string argument followed by a list of printf |
---|
413 | arguments from its variable argument list, and convert |
---|
414 | these into a string. |
---|
415 | This type code can only occur as the last element |
---|
416 | of <tt>exc_argformat</tt>. |
---|
417 | </dl> |
---|
418 | <p> |
---|
419 | <dt> |
---|
420 | <tt> void (*etype_print)(SM_EXC_T *exc, SM_FILE_T *stream) </tt> |
---|
421 | <dd> |
---|
422 | This function prints an exception of the specified type |
---|
423 | onto an output stream. |
---|
424 | The final character printed is not a newline. |
---|
425 | </dl> |
---|
426 | |
---|
427 | <h2> Standard Exceptions and Exception Types </h2> |
---|
428 | |
---|
429 | Libsm defines one standard exception value, <tt>SmHeapOutOfMemory</tt>. |
---|
430 | This is a statically initialized const variable, because it seems |
---|
431 | like a bad idea to dynamically allocate an exception object to |
---|
432 | report a low memory condition. |
---|
433 | This exception has category <tt>"F:sm.heap"</tt>. |
---|
434 | If you need to, you can explicitly raise this exception |
---|
435 | with <tt>sm_exc_raise_x(&SmHeapOutOfMemory)</tt>. |
---|
436 | |
---|
437 | <p> |
---|
438 | Statically initialized exception values cannot contain any |
---|
439 | run-time parameters, so the normal case is to dynamically allocate |
---|
440 | a new exception object whenever you raise an exception. |
---|
441 | Before you can create an exception, you need an exception type. |
---|
442 | Libsm defines the following standard exception types. |
---|
443 | |
---|
444 | <dl> |
---|
445 | <dt> |
---|
446 | <tt> SmEtypeOs </tt> |
---|
447 | <dd> |
---|
448 | This represents a generic operating system error. |
---|
449 | The category is <tt>"E:sm.os"</tt>. |
---|
450 | The argformat is <tt>"isr"</tt>, |
---|
451 | where argv[0] is the value of <tt>errno</tt> |
---|
452 | after a system call has failed, |
---|
453 | argv[1] is the name of the function (usually a system call) that failed, |
---|
454 | and argv[2] is either <tt>NULL</tt> |
---|
455 | or a character string which describes some of the arguments |
---|
456 | to the failing system call (usually it is just a file name). |
---|
457 | Here's an example of raising an exception: |
---|
458 | |
---|
459 | <blockquote><pre> |
---|
460 | fd = open(filename, O_RDONLY); |
---|
461 | if (fd == -1) |
---|
462 | sm_exc_raisenew_x(&SmEtypeOs, errno, "open", "%s", filename); |
---|
463 | </pre></blockquote> |
---|
464 | |
---|
465 | If errno is ENOENT and filename is "/etc/mail/snedmail.cf", |
---|
466 | then the exception raised by the above code will be printed as |
---|
467 | |
---|
468 | <blockquote><pre> |
---|
469 | /etc/mail/snedmail.cf: open failed: No such file or directory |
---|
470 | </pre></blockquote> |
---|
471 | |
---|
472 | <dt> |
---|
473 | <tt> SmEtypeErr </tt> |
---|
474 | <dd> |
---|
475 | This represents a generic error. |
---|
476 | The category is <tt>"E:sm.err"</tt>, |
---|
477 | and the argformat is <tt>"r"</tt>. |
---|
478 | You can use it |
---|
479 | in application contexts where you are raising an exception |
---|
480 | for the purpose of terminating the program. |
---|
481 | You know the exception won't be handled, |
---|
482 | so you don't need to worry about packaging the error for |
---|
483 | later analysis by an exception handler. |
---|
484 | All you need to specify is the message string that |
---|
485 | will be printed to stderr before the program exits. |
---|
486 | For example, |
---|
487 | |
---|
488 | <blockquote><pre> |
---|
489 | sm_exc_raisenew_x(&SmEtypeErr, "name lookup failed: %s", name); |
---|
490 | </pre></blockquote> |
---|
491 | </dl> |
---|
492 | |
---|
493 | <h2> Custom Exception Types </h2> |
---|
494 | |
---|
495 | If you are writing a library package, and you need to raise |
---|
496 | exceptions that are not standard Unix system errors, |
---|
497 | then you need to define one or more new exception types. |
---|
498 | |
---|
499 | <p> |
---|
500 | Every new exception type needs a print function. |
---|
501 | The standard print function <tt>sm_etype_printf</tt> |
---|
502 | is all you need in the majority of cases. |
---|
503 | It prints the <tt>etype_printcontext</tt> string of the exception type, |
---|
504 | substituting occurrences of %0 through %9 with the corresponding |
---|
505 | exception argument. |
---|
506 | If exception argument 3 is an int or long, |
---|
507 | then %3 will print the argument in decimal, |
---|
508 | and %o3 or %x3 will print it in octal or hex. |
---|
509 | |
---|
510 | <p> |
---|
511 | In the following example, I will assume that your library |
---|
512 | package implements regular expressions, and can raise 5 different exceptions. |
---|
513 | When compiling a regular expression, 3 different syntax errors |
---|
514 | can be reported: |
---|
515 | <ul> |
---|
516 | <li>unbalanced parenthesis |
---|
517 | <li>unbalanced bracket |
---|
518 | <li>missing argument for repetition operator |
---|
519 | </ul> |
---|
520 | Whenever one of these errors is reported, you will also report |
---|
521 | the index of the character within the regex string at which the |
---|
522 | syntax error was detected. |
---|
523 | The fourth exception is raised if a compiled regular expression |
---|
524 | is invalid: this exception has no arguments. |
---|
525 | The fifth exception is raised if the package runs out of memory: |
---|
526 | for this, you use the standard <tt>SmHeapOutOfMemory</tt> exception. |
---|
527 | |
---|
528 | <p> |
---|
529 | The obvious approach is to define 4 separate exception types. |
---|
530 | Here they are: |
---|
531 | |
---|
532 | <blockquote><pre> |
---|
533 | /* print a regular expression syntax error */ |
---|
534 | void |
---|
535 | rx_esyntax_print(SM_EXC_T *exc, SM_FILE_T *stream) |
---|
536 | { |
---|
537 | sm_io_fprintf(stream, "rx syntax error at character %d: %s", |
---|
538 | exc->exc_argv[0].v_int, |
---|
539 | exc->exc_type->etype_printcontext); |
---|
540 | } |
---|
541 | SM_EXC_TYPE_T RxSyntaxParen = { |
---|
542 | SmExcTypeMagic, |
---|
543 | "E:mylib.rx.syntax.paren", |
---|
544 | "i", |
---|
545 | rx_esyntax_print, |
---|
546 | "unbalanced parenthesis" |
---|
547 | }; |
---|
548 | SM_EXC_TYPE_T RxSyntaxBracket = { |
---|
549 | SmExcTypeMagic, |
---|
550 | "E:mylib.rx.syntax.bracket", |
---|
551 | "i", |
---|
552 | rx_esyntax_print, |
---|
553 | "unbalanced bracket" |
---|
554 | }; |
---|
555 | SM_EXC_TYPE_T RxSyntaxMissingArg = { |
---|
556 | SmExcTypeMagic, |
---|
557 | "E:mylib.rx.syntax.missingarg", |
---|
558 | "i", |
---|
559 | rx_esyntax_print, |
---|
560 | "missing argument for repetition operator" |
---|
561 | }; |
---|
562 | |
---|
563 | SM_EXC_TYPE_T RxRunCorrupt = { |
---|
564 | SmExcTypeMagic, |
---|
565 | "E:mylib.rx.run.corrupt", |
---|
566 | "", |
---|
567 | sm_etype_printf, |
---|
568 | "rx runtime error: compiled regular expression is corrupt" |
---|
569 | }; |
---|
570 | </pre></blockquote> |
---|
571 | |
---|
572 | <p> |
---|
573 | With the above definitions, you can raise a syntax error reporting |
---|
574 | an unbalanced parenthesis at string offset <tt>i</tt> using: |
---|
575 | <blockquote><pre> |
---|
576 | sm_exc_raisenew_x(&RxSyntaxParen, i); |
---|
577 | </pre></blockquote> |
---|
578 | |
---|
579 | If <tt>i==42</tt> then this exception will be printed as: |
---|
580 | <blockquote><pre> |
---|
581 | rx syntax error at character 42: unbalanced parenthesis |
---|
582 | </pre></blockquote> |
---|
583 | |
---|
584 | An exception handler can provide special handling for regular |
---|
585 | expression syntax errors using this code: |
---|
586 | <blockquote><pre> |
---|
587 | SM_TRY |
---|
588 | ... code that might raise an exception ... |
---|
589 | SM_EXCEPT(exc, "E:mylib.rx.syntax.*") |
---|
590 | int i = exc->exc_argv[0].v_int; |
---|
591 | ... handle a regular expression syntax error ... |
---|
592 | SM_END_TRY |
---|
593 | </pre></blockquote> |
---|
594 | |
---|
595 | <p> |
---|
596 | External requirements may force you to define an integer code |
---|
597 | for each error reported by your package. Or you may be wrapping |
---|
598 | an existing package that works this way. In this case, it might |
---|
599 | make sense to define a single exception type, patterned after SmEtypeOs, |
---|
600 | and include the integer code as an exception argument. |
---|
601 | |
---|
602 | <p> |
---|
603 | Your package might intercept an exception E generated by a lower |
---|
604 | level package, and then reclassify it as a different expression E'. |
---|
605 | For example, a package for reading a configuration file might |
---|
606 | reclassify one of the regular expression syntax errors from the |
---|
607 | previous example as a configuration file syntax error. |
---|
608 | When you do this, the new exception E' should include the original |
---|
609 | exception E as an exception parameter, and the print function for |
---|
610 | exception E' should print the high level description of the exception |
---|
611 | (eg, "syntax error in configuration file %s at line %d\n"), |
---|
612 | then print the subexception that is stored as an exception parameter. |
---|
613 | |
---|
614 | <h2> Function Reference </h2> |
---|
615 | |
---|
616 | <dl> |
---|
617 | <dt> |
---|
618 | <tt> SM_EXC_T *sm_exc_new_x(const SM_EXC_TYPE_T *type, ...) </tt> |
---|
619 | <dd> |
---|
620 | Create a new exception. Raise an exception on heap exhaustion. |
---|
621 | The new exception has a reference count of 1. |
---|
622 | <p> |
---|
623 | |
---|
624 | A list of zero or more exception arguments follows the exception type; |
---|
625 | these are copied into the new exception object. |
---|
626 | The number and types of these arguments is determined |
---|
627 | by <tt>type->etype_argformat</tt>. |
---|
628 | <p> |
---|
629 | |
---|
630 | Note that there is no rpool argument to sm_exc_new_x. |
---|
631 | Exceptions are allocated directly from the heap. |
---|
632 | This is because exceptions are normally raised at low levels |
---|
633 | of abstraction and handled at high levels. Because the low |
---|
634 | level code typically has no idea of how or at what level the |
---|
635 | exception will be handled, it also has no idea of which resource |
---|
636 | pool, if any, should own the exception. |
---|
637 | <p> |
---|
638 | <dt> |
---|
639 | <tt> SM_EXC_T *sm_exc_addref(SM_EXC_T *exc) </tt> |
---|
640 | <dd> |
---|
641 | Increment the reference count of an exception. |
---|
642 | Return the first argument. |
---|
643 | <p> |
---|
644 | <dt> |
---|
645 | <tt> void sm_exc_free(SM_EXC_T *exc) </tt> |
---|
646 | <dd> |
---|
647 | Decrement the reference count of an exception. |
---|
648 | If it reaches 0, free the exception object. |
---|
649 | <p> |
---|
650 | <dt> |
---|
651 | <tt> bool sm_exc_match(SM_EXC_T *exc, const char *pattern) </tt> |
---|
652 | <dd> |
---|
653 | Compare the exception's category to the specified glob pattern, |
---|
654 | return true if they match. |
---|
655 | <p> |
---|
656 | <dt> |
---|
657 | <tt> void sm_exc_print(SM_EXC_T *exc, SM_FILE_T *stream) </tt> |
---|
658 | <dd> |
---|
659 | Print the exception on the stream |
---|
660 | as a sequence of one or more newline terminated lines. |
---|
661 | <p> |
---|
662 | <dt> |
---|
663 | <tt> void sm_exc_write(SM_EXC_T *exc, SM_FILE_T *stream) </tt> |
---|
664 | <dd> |
---|
665 | Write the exception on the stream without a terminating newline. |
---|
666 | <p> |
---|
667 | <dt> |
---|
668 | <tt> void sm_exc_raise_x(SM_EXC_T *exc) </tt> |
---|
669 | <dd> |
---|
670 | Raise the exception. This function does not return to its caller. |
---|
671 | <p> |
---|
672 | <dt> |
---|
673 | <tt> void sm_exc_raisenew_x(const SM_EXC_TYPE_T *type, ...) </tt> |
---|
674 | <dd> |
---|
675 | A short form for <tt>sm_exc_raise_x(sm_exc_new_x(type,...))</tt>. |
---|
676 | </dl> |
---|
677 | |
---|
678 | <h2> Macro Reference </h2> |
---|
679 | |
---|
680 | The SM_TRY ... SM_END_TRY control structure |
---|
681 | ensures that cleanup code is executed in the presence of exceptions, |
---|
682 | and permits exceptions to be handled. |
---|
683 | |
---|
684 | <blockquote><pre> |
---|
685 | SM_TRY |
---|
686 | A block of code that may raise an exception. |
---|
687 | SM_FINALLY |
---|
688 | Cleanup code that may raise an exception. |
---|
689 | This code is guaranteed to be executed whether or not |
---|
690 | an exception was raised by a previous clause. |
---|
691 | You may have 0 or more SM_FINALLY clauses. |
---|
692 | SM_EXCEPT(e, pat) |
---|
693 | Exception handling code, which is triggered by an exception |
---|
694 | whose category matches the glob pattern 'pat'. |
---|
695 | The exception value is bound to the local variable 'e'. |
---|
696 | You may have 0 or more SM_EXCEPT clauses. |
---|
697 | SM_END_TRY |
---|
698 | </pre></blockquote> |
---|
699 | |
---|
700 | First, the SM_TRY clause is executed, then each SM_FINALLY clause is |
---|
701 | executed in sequence. |
---|
702 | If one or more of these clauses was terminated by an exception, |
---|
703 | then the first such exception is remembered, and the other exceptions |
---|
704 | are lost. |
---|
705 | |
---|
706 | If no exception was raised, then we are done. |
---|
707 | |
---|
708 | Otherwise, each of the SM_EXCEPT clauses is examined in sequence. |
---|
709 | and the first SM_EXCEPT clause whose pattern argument matches the exception |
---|
710 | (see <tt>sm_exc_match</tt>) is executed. |
---|
711 | If none of the SM_EXCEPT clauses matched the exception, or if there are |
---|
712 | no SM_EXCEPT clauses, then the remembered exception is re-raised. |
---|
713 | |
---|
714 | <p> |
---|
715 | SM_TRY .. SM_END_TRY clauses may be nested arbitrarily. |
---|
716 | |
---|
717 | <p> |
---|
718 | It is illegal to jump out of a SM_TRY or SM_FINALLY clause |
---|
719 | using goto, break, continue, return or longjmp. |
---|
720 | If you do this, you will corrupt the internal exception handling stack. |
---|
721 | You can't use <tt>break</tt> or <tt>continue</tt> in an SM_EXCEPT clause; |
---|
722 | these are reserved for use by the implementation. |
---|
723 | It is legal to jump out of an SM_EXCEPT clause using goto or return; |
---|
724 | however, in this case, you must take responsibility |
---|
725 | for freeing the exception object. |
---|
726 | |
---|
727 | <p> |
---|
728 | The SM_TRY and SM_FINALLY macros contain calls to setjmp, |
---|
729 | and consequently, they suffer from the limitations imposed on setjmp |
---|
730 | by the C standard. |
---|
731 | Suppose you declare an auto variable <tt>i</tt> outside of a |
---|
732 | SM_TRY ... SM_END_TRY statement, initializing it to 0. |
---|
733 | Then you modify <tt>i</tt> inside of a SM_TRY or SM_FINALLY clause, |
---|
734 | setting it to 1. |
---|
735 | If you reference <tt>i</tt> in a different SM_FINALLY clause, or in |
---|
736 | an SM_EXCEPT clause, then it is implementation dependent whether <tt>i</tt> |
---|
737 | will be 0 or 1, unless you have declared <tt>i</tt> to be <tt>volatile</tt>. |
---|
738 | |
---|
739 | <blockquote><pre> |
---|
740 | int volatile i = 0; |
---|
741 | |
---|
742 | SM_TRY |
---|
743 | i = 1; |
---|
744 | ... |
---|
745 | SM_FINALLY |
---|
746 | /* the following reference to i only works if i is declared volatile */ |
---|
747 | use(i); |
---|
748 | ... |
---|
749 | SM_EXCEPT(exc, "*") |
---|
750 | /* the following reference to i only works if i is declared volatile */ |
---|
751 | use(i); |
---|
752 | ... |
---|
753 | SM_END_TRY |
---|
754 | </pre></blockquote> |
---|
755 | |
---|
756 | </body> |
---|
757 | </html> |
---|