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.13 2006/06/20 17:18:16 ca Exp $ 12</center> 13 14<h2> Introduction </h2> 15 16The exception handling package provides the facilities that 17functions in libsm use to report errors. 18Here 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 then 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 55typedef struct sm_exc_type SM_EXC_TYPE_T; 56typedef struct sm_exc SM_EXC_T; 57typedef union sm_val SM_VAL_T; 58 59/* 60** Exception types 61*/ 62 63extern const char SmExcTypeMagic[]; 64 65struct 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 74extern const SM_EXC_TYPE_T SmEtypeOs; 75extern const SM_EXC_TYPE_T SmEtypeErr; 76 77void 78sm_etype_printf( 79 SM_EXC_T *exc, 80 SM_FILE_T *stream); 81 82/* 83** Exception objects 84*/ 85 86extern const char SmExcMagic[]; 87 88union sm_val 89{ 90 int v_int; 91 long v_long; 92 char *v_str; 93 SM_EXC_T *v_exc; 94}; 95 96struct 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 104SM_EXC_T * 105sm_exc_new_x( 106 const SM_EXC_TYPE_T *type, 107 ...); 108 109SM_EXC_T * 110sm_exc_addref( 111 SM_EXC_T *exc); 112 113void 114sm_exc_free( 115 SM_EXC_T *exc); 116 117bool 118sm_exc_match( 119 SM_EXC_T *exc, 120 const char *pattern); 121 122void 123sm_exc_print( 124 SM_EXC_T *exc, 125 SM_FILE_T *stream); 126 127void 128sm_exc_write( 129 SM_EXC_T *exc, 130 SM_FILE_T *stream); 131 132void 133sm_exc_raise_x( 134 SM_EXC_T *exc); 135 136void 137sm_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*/ 145SM_TRY 146 Block of code that may raise an exception. 147SM_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. 152SM_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. 156SM_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> 189fd = open(path, O_RDONLY); 190if (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> 206errx(1, "%s:%d: syntax error", filename, lineno); 207</pre></blockquote> 208 209 use this: 210 211<blockquote><pre> 212sm_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> 229rpool = sm_rpool_new_x(&SmRpoolRoot, 0); 230... some code ... 231sm_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> 241rpool = sm_rpool_new_x(&SmRpoolRoot, 0); 242SM_TRY 243 ... some code that can raise an exception ... 244SM_FINALLY 245 sm_rpool_free_x(rpool); 246SM_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> 256SM_TRY 257 /* code that can raise an exception */ 258 ... 259SM_EXCEPT(exc, "*") 260 /* catch all exceptions */ 261 sm_exc_print(exc, stderr); 262SM_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> 279SM_TRY 280 /* code that might raise end-of-file, or some other exception */ 281 ... 282SM_EXCEPT(exc, "E:sm.eof") 283 /* what to do if end-of-file is encountered */ 284 ... 285SM_EXCEPT(exc, "*") 286 /* what to do if some other exception is raised */ 287 ... 288SM_END_TRY 289</pre></blockquote> 290</ol> 291 292<h2> Exception Values </h2> 293 294In traditional C code, errors are usually denoted by a single integer, 295such as errno. In practice, errno does not carry enough information 296to describe everything that an error handler might want to know about 297an error. And the scheme is not very extensible: if several different 298packages want to add additional error codes, it is hard to avoid 299collisions. 300 301<p> 302In libsm, an exceptional condition is described 303by an object of type SM_EXC_T. 304An exception object is created by specifying an exception type 305and a list of exception arguments. 306 307<p> 308The exception arguments are an array of zero or more values. 309The values may be a mixture of ints, longs, strings, and exceptions. 310In the SM_EXC_T structure, the argument vector is represented 311by <tt>SM_VAL_T *exc_argv</tt>, where <tt>SM_VAL_T</tt> 312is a union of the possible argument types. 313The number and types of exception arguments is determined by 314the exception type. 315 316<p> 317An exception type is a statically initialized const object 318of 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 429Libsm defines one standard exception value, <tt>SmHeapOutOfMemory</tt>. 430This is a statically initialized const variable, because it seems 431like a bad idea to dynamically allocate an exception object to 432report a low memory condition. 433This exception has category <tt>"F:sm.heap"</tt>. 434If you need to, you can explicitly raise this exception 435with <tt>sm_exc_raise_x(&SmHeapOutOfMemory)</tt>. 436 437<p> 438Statically initialized exception values cannot contain any 439run-time parameters, so the normal case is to dynamically allocate 440a new exception object whenever you raise an exception. 441Before you can create an exception, you need an exception type. 442Libsm 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> 460fd = open(filename, O_RDONLY); 461if (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> 489sm_exc_raisenew_x(&SmEtypeErr, "name lookup failed: %s", name); 490</pre></blockquote> 491</dl> 492 493<h2> Custom Exception Types </h2> 494 495If you are writing a library package, and you need to raise 496exceptions that are not standard Unix system errors, 497then you need to define one or more new exception types. 498 499<p> 500Every new exception type needs a print function. 501The standard print function <tt>sm_etype_printf</tt> 502is all you need in the majority of cases. 503It prints the <tt>etype_printcontext</tt> string of the exception type, 504substituting occurrences of %0 through %9 with the corresponding 505exception argument. 506If exception argument 3 is an int or long, 507then %3 will print the argument in decimal, 508and %o3 or %x3 will print it in octal or hex. 509 510<p> 511In the following example, I will assume that your library 512package implements regular expressions, and can raise 5 different exceptions. 513When compiling a regular expression, 3 different syntax errors 514can be reported: 515<ul> 516<li>unbalanced parenthesis 517<li>unbalanced bracket 518<li>missing argument for repetition operator 519</ul> 520Whenever one of these errors is reported, you will also report 521the index of the character within the regex string at which the 522syntax error was detected. 523The fourth exception is raised if a compiled regular expression 524is invalid: this exception has no arguments. 525The fifth exception is raised if the package runs out of memory: 526for this, you use the standard <tt>SmHeapOutOfMemory</tt> exception. 527 528<p> 529The obvious approach is to define 4 separate exception types. 530Here they are: 531 532<blockquote><pre> 533/* print a regular expression syntax error */ 534void 535rx_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} 541SM_EXC_TYPE_T RxSyntaxParen = { 542 SmExcTypeMagic, 543 "E:mylib.rx.syntax.paren", 544 "i", 545 rx_esyntax_print, 546 "unbalanced parenthesis" 547}; 548SM_EXC_TYPE_T RxSyntaxBracket = { 549 SmExcTypeMagic, 550 "E:mylib.rx.syntax.bracket", 551 "i", 552 rx_esyntax_print, 553 "unbalanced bracket" 554}; 555SM_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 563SM_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> 573With the above definitions, you can raise a syntax error reporting 574an unbalanced parenthesis at string offset <tt>i</tt> using: 575<blockquote><pre> 576sm_exc_raisenew_x(&RxSyntaxParen, i); 577</pre></blockquote> 578 579If <tt>i==42</tt> then this exception will be printed as: 580<blockquote><pre> 581rx syntax error at character 42: unbalanced parenthesis 582</pre></blockquote> 583 584An exception handler can provide special handling for regular 585expression syntax errors using this code: 586<blockquote><pre> 587SM_TRY 588 ... code that might raise an exception ... 589SM_EXCEPT(exc, "E:mylib.rx.syntax.*") 590 int i = exc->exc_argv[0].v_int; 591 ... handle a regular expression syntax error ... 592SM_END_TRY 593</pre></blockquote> 594 595<p> 596External requirements may force you to define an integer code 597for each error reported by your package. Or you may be wrapping 598an existing package that works this way. In this case, it might 599make sense to define a single exception type, patterned after SmEtypeOs, 600and include the integer code as an exception argument. 601 602<p> 603Your package might intercept an exception E generated by a lower 604level package, and then reclassify it as a different expression E'. 605For example, a package for reading a configuration file might 606reclassify one of the regular expression syntax errors from the 607previous example as a configuration file syntax error. 608When you do this, the new exception E' should include the original 609exception E as an exception parameter, and the print function for 610exception E' should print the high level description of the exception 611(eg, "syntax error in configuration file %s at line %d\n"), 612then 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 680The SM_TRY ... SM_END_TRY control structure 681ensures that cleanup code is executed in the presence of exceptions, 682and permits exceptions to be handled. 683 684<blockquote><pre> 685SM_TRY 686 A block of code that may raise an exception. 687SM_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. 692SM_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. 697SM_END_TRY 698</pre></blockquote> 699 700First, the SM_TRY clause is executed, then each SM_FINALLY clause is 701executed in sequence. 702If one or more of these clauses was terminated by an exception, 703then the first such exception is remembered, and the other exceptions 704are lost. 705 706If no exception was raised, then we are done. 707 708Otherwise, each of the SM_EXCEPT clauses is examined in sequence. 709and the first SM_EXCEPT clause whose pattern argument matches the exception 710(see <tt>sm_exc_match</tt>) is executed. 711If none of the SM_EXCEPT clauses matched the exception, or if there are 712no SM_EXCEPT clauses, then the remembered exception is re-raised. 713 714<p> 715SM_TRY .. SM_END_TRY clauses may be nested arbitrarily. 716 717<p> 718It is illegal to jump out of a SM_TRY or SM_FINALLY clause 719using goto, break, continue, return or longjmp. 720If you do this, you will corrupt the internal exception handling stack. 721You can't use <tt>break</tt> or <tt>continue</tt> in an SM_EXCEPT clause; 722these are reserved for use by the implementation. 723It is legal to jump out of an SM_EXCEPT clause using goto or return; 724however, in this case, you must take responsibility 725for freeing the exception object. 726 727<p> 728The SM_TRY and SM_FINALLY macros contain calls to setjmp, 729and consequently, they suffer from the limitations imposed on setjmp 730by the C standard. 731Suppose you declare an auto variable <tt>i</tt> outside of a 732SM_TRY ... SM_END_TRY statement, initializing it to 0. 733Then you modify <tt>i</tt> inside of a SM_TRY or SM_FINALLY clause, 734setting it to 1. 735If you reference <tt>i</tt> in a different SM_FINALLY clause, or in 736an SM_EXCEPT clause, then it is implementation dependent whether <tt>i</tt> 737will be 0 or 1, unless you have declared <tt>i</tt> to be <tt>volatile</tt>. 738 739<blockquote><pre> 740int volatile i = 0; 741 742SM_TRY 743 i = 1; 744 ... 745SM_FINALLY 746 /* the following reference to i only works if i is declared volatile */ 747 use(i); 748 ... 749SM_EXCEPT(exc, "*") 750 /* the following reference to i only works if i is declared volatile */ 751 use(i); 752 ... 753SM_END_TRY 754</pre></blockquote> 755 756</body> 757</html> 758