summaryrefslogtreecommitdiff
path: root/more/generic_exception_safety.html
blob: 1d03d9387a16782ac0d39ad3b092253cbbe4a6cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0052)http://people.ne.mediaone.net/abrahams/abrahams.html -->

    <meta name="generator" content="Microsoft FrontPage 5.0">
    <title>Exception-Safety in Generic Components</title>
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
    <meta content="MSHTML 5.50.4522.1800" name="GENERATOR">

    <h1 align="center">Exception-Safety in Generic Components</h1>

    <p align="center"><i><b>Lessons Learned from Specifying Exception-Safety
    for the C++ Standard Library</b></i>

    <h3 align="center">David Abrahams</h3>

    <h3 align="center"><a href="mailto:david.abrahams@rcn.com">
    david.abrahams@rcn.com</a></h3>

    <p><b>Abstract.</b> This paper represents the knowledge accumulated in
    response to a real-world need: that the C++ Standard Template Library
    exhibit useful and well-defined interactions with exceptions, the
    error-handling mechanism built-in to the core C++ language. It explores the
    meaning of exception-safety, reveals surprising myths about exceptions and
    genericity, describes valuable tools for reasoning about program
    correctness, and outlines an automated testing procedure for verifying
    exception-safety.

    <p><b>Keywords:</b> exception-safety, exceptions, STL, C++

    <h2>1 What is exception-safety?</h2>

    <p>Informally, exception-safety in a component means that it exhibits
    reasonable behavior when an exception is thrown during its execution. For
    most people, the term ``reasonable'' includes all the usual
    expectations for error-handling: that resources should not be leaked, and
    that the program should remain in a well-defined state so that execution
    can continue. For most components, it also includes the expectation that
    when an error is encountered, it is reported to the caller.

    <p>More formally, we can describe a component as minimally exception-safe
    if, when exceptions are thrown from within that component, its invariants
    are intact. Later on we'll see that at least three different levels of
    exception-safety can be usefully distinguished. These distinctions can help
    us to describe and reason about the behavior of large systems.

    <p>In a generic component, we usually have an additional expectation of
    <i>exception-neutrality</i>, which means that exceptions thrown by a
    component's type parameters should be propagated, unchanged, to the
    component's caller.

    <h2>2 Myths and Superstitions</h2>

    <p>Exception-safety seems straightforward so far: it doesn't constitute
    anything more than we'd expect from code using more traditional
    error-handling techniques. It might be worthwhile, however, to examine the
    term from a psychological viewpoint. Nobody ever spoke of
    ``error-safety'' before C++ had exceptions.

    <p>It's almost as though exceptions are viewed as a <i>mysterious
    attack</i> on otherwise correct code, from which we must protect ourselves.
    Needless to say, this doesn't lead to a healthy relationship with error
    handling! During standardization, a democratic process which requires broad
    support for changes, I encountered many widely-held superstitions. In order
    to even begin the discussion of exception-safety in generic components, it
    may be worthwhile confronting a few of them.

    <p><i>``Interactions between templates and exceptions are not
    well-understood.''</i> This myth, often heard from those who consider
    these both new language features, is easily disposed of: there simply are
    no interactions. A template, once instantiated, works in all respects like
    an ordinary class or function. A simple way to reason about the behavior of
    a template with exceptions is to think of how a specific instantiation of
    that template works. Finally, the genericity of templates should not cause
    special concern. Although the component's client supplies part of the
    operation (which may, unless otherwise specified, throw arbitrary
    exceptions), the same is true of operations using familiar virtual
    functions or simple function pointers.

    <p><i>``It is well known to be impossible to write an exception-safe
    generic container.''</i> This claim is often heard with reference to
    an article by Tom Cargill <a title=
    "Tom Cargill, ``Exception Handling: A False Sense of Security'', C++ Report, Nov-Dec 1994"
     href=
    "#reference4"><sup>[4]</sup></a>
    in which he explores the problem of exception-safety for a generic stack
    template. In his article, Cargill raises many useful questions, but
    unfortunately fails to present a solution to his problem.<a title=
    "Probably the greatest impediment to a solution in Cargill's case was an unfortunate combination of choices on his part: the interface he chose for his container was incompatible with his particular demands for safety. By changing either one he might have solved the problem."
     href=
    "#footnote1"><sup>1</sup></a>
    He concludes by suggesting that a solution may not be possible.
    Unfortunately, his article was read by many as ``proof'' of that
    speculation. Since it was published there have been many examples of
    exception-safe generic components, among them the C++ standard library
    containers.

    <p><i>``Dealing with exceptions will slow code down, and templates are
    used specifically to get the best possible performance.''</i> A good
    implementation of C++ will not devote a single instruction cycle to dealing
    with exceptions until one is thrown, and then it can be handled at a speed
    comparable with that of calling a function <a title=
    "D. R. Musser, ``Introspective Sorting and Selection Algorithms'', Software-Practice and Experience 27(8):983-993, 1997."
     href=
    "#reference7"><sup>[7]</sup></a>.
    That alone gives programs using exceptions performance equivalent to that
    of a program which ignores the possibility of errors. Using exceptions can
    actually result in faster programs than ``traditional'' error
    handling methods for other reasons. First, a catch block clearly indicates
    to the compiler which code is devoted to error-handling; it can then be
    separated from the usual execution path, improving locality of reference.
    Second, code using ``traditional'' error handling must typically
    test a return value for errors after every single function call; using
    exceptions completely eliminates that overhead.

    <p><i>``Exceptions make it more difficult to reason about a program's
    behavior.''</i> Usually cited in support of this myth is the way
    ``hidden'' execution paths are followed during stack-unwinding.
    Hidden execution paths are nothing new to any C++ programmer who expects
    local variables to be destroyed upon returning from a function:

    <blockquote>
<pre>ErrorCode f( int&amp; result )         // 1 
{                                  // 2 
    X x;                           // 3 
    ErrorCode err = x.g( result ); // 4 
    if ( err != kNoError )         // 5 
        return err;                // 6 
    // ...More code here... 
    return kNoError;               // 7 
}
</pre>
    </blockquote>

    <p>In the example above, there is a ``hidden'' call to
    <code>X::~X()</code> in lines 6 and 7. Granted, using exceptions, there is
    no code devoted to error handling visible:

    <blockquote>
<pre>int f()                 // 1 
{                       // 2 
    X x;                // 3 
    int result = x.g(); // 4 
    // ...More code here... 
    return result;      // 5 
} 
</pre>
    </blockquote>

    <p>For many programmers more familiar with exceptions, the second example
    is actually more readable and understandable than the first. The
    ``hidden'' code paths include the same calls to destructors of
    local variables. In addition, they follow a simple pattern which acts
    <i>exactly</i> as though there were a potential return statement after each
    function call in case of an exception. Readability is enhanced because the
    normal path of execution is unobscured by error-handling, and return values
    are freed up to be used in a natural way.

    <p>There is an even more important way in which exceptions can enhance
    correctness: by allowing simple class invariants. In the first example, if
    <code>x</code>'s constructor should need to allocate resources, it has no
    way to report a failure: in C++, constructors have no return values. The
    usual result when exceptions are avoided is that classes requiring
    resources must include a separate initializer function which finishes the
    job of construction. The programmer can therefore never be sure, when an
    object of class <code>X</code> is used, whether he is handling a
    full-fledged <code>X</code> or some abortive attempt to construct one (or
    worse: someone simply forgot to call the initializer!)

    <h2>3 A contractual basis for exception-safety</h2>

    <p>A non-generic component can be described as exception-safe in isolation,
    but because of its configurability by client code, exception-safety in a
    generic component usually depends on a contract between the component and
    its clients. For example, the designer of a generic component might require
    that an operation which is used in the component's destructor not throw any
    exceptions.<a title=
    " It is usually inadvisable to throw an exception from a destructor in C++, since the destructor may itself be called during the stack-unwinding caused by another exception. If the second exception is allowed to propagate beyond the destructor, the program is immediately terminated."
     href=
    "#footnote2"><sup>2</sup></a>
    The generic component might, in return, provide one of the following
    guarantees:

    <ul>
      <li>The <i>basic</i> guarantee: that the invariants of the component are
      preserved, and no resources are leaked.

      <li>The <i>strong</i> guarantee: that the operation has either completed
      successfully or thrown an exception, leaving the program state exactly as
      it was before the operation started.

      <li>The <i>no-throw</i> guarantee: that the operation will not throw an
      exception.
    </ul>

    <p>The basic guarantee is a simple minimum standard for exception-safety to
    which we can hold all components. It says simply that after an exception,
    the component can still be used as before. Importantly, the preservation of
    invariants allows the component to be destroyed, potentially as part of
    stack-unwinding. This guarantee is actually less useful than it might at
    first appear. If a component has many valid states, after an exception we
    have no idea what state the component is in|only that the state is valid.
    The options for recovery in this case are limited: either destruction or
    resetting the component to some known state before further use. Consider
    the following example:

    <blockquote>
<pre>template &lt;class X&gt; 
void print_random_sequence() 
{ 
    std::vector&lt;X&gt; v(10); // A vector of 10 items 
    try { 
        // Provides only the <i>basic</i> guarantee 
        v.insert( v.begin(), X() ); 
    } 
    catch(...) {} // ignore any exceptions above 
    // print the vector's contents 
    std::cout "(" &lt;&lt; v.size() &lt;&lt; ") "; 
    std::copy( v.begin(), v.end(), 
    std::ostream_iterator&lt;X&gt;( std::cout, " " ) ); 
} 
</pre>
    </blockquote>

    <p>Since all we know about v after an exception is that it is valid, the
    function is allowed to print any random sequence of <code>X</code>s.<a
    title=
    "In practice of course, this function would make an extremely poor random sequence generator!"
     href=
    "#footnote3"><sup>3</sup></a>
    It is ``safe'' in the sense that it is not allowed to crash, but
    its output may be unpredictable.

    <p>The <i>strong</i> guarantee provides full
    ``commit-or-rollback'' semantics. In the case of C++ standard
    containers, this means, for example, that if an exception is thrown all
    iterators remain valid. We also know that the container has exactly the
    same elements as before the exception was thrown. A transaction that has no
    effects if it fails has obvious benefits: the program state is simple and
    predictable in case of an exception. In the C++ standard library, nearly
    all of the operations on the node-based containers list, set, multiset,
    map, and multimap provide the <i>strong</i> guarantee.<a title=
    "It is worth noting that mutating algorithms usually cannot provide the strong guarantee: to roll back a modified element of a range, it must be set back to its previous value using operator=, which itself might throw. In the C++ standard library, there are a few exceptions to this rule, whose rollback behavior consists only of destruction: uninitialized_copy, uninitialized_fill, and uninitialized_fill_n."
     href=
    "#footnote4"><sup>4</sup></a>).

    <p>The <i>no-throw</i> guarantee is the strongest of all, and it says that
    an operation is guaranteed not to throw an exception: it always completes
    successfully. This guarantee is necessary for most destructors, and indeed
    the destructors of C++ standard library components are all guaranteed not
    to throw exceptions. The <i>no-throw</i> guarantee turns out to be
    important for other reasons, as we shall see.<a title=
    "All type parameters supplied by clients of the C++ standard library are required not to throw from their destructors. In return, all components of the C++ standard library provide at least the basic guarantee."
     href=
    "#footnote5"><sup>5</sup></a>

    <h2>4 Legal Wrangling</h2>

    <p>Inevitably, the contract can get more complicated: a quid pro quo
    arrangement is possible. Some components in the C++ Standard Library give
    one guarantee for arbitrary type parameters, but give a stronger guarantee
    in exchange for additional promises from the client type that no exceptions
    will be thrown. For example, the standard container operation
    <code>vector&lt;T&gt;::erase</code> gives the <i>basic</i> guarantee for
    any <code>T</code>, but for types whose copy constructor and copy
    assignment operator do not throw, it gives the <i>no-throw</i> guarantee.<a
    title=
    "Similar arrangements might have been made in the C++ standard for many of the mutating algorithms, but were never considered due to time constraints on the standardization process."
     href=
    "#footnote6"><sup>6</sup></a>

    <h2>5 What level of exception-safety should a component specify?</h2>

    <p>From a client's point-of-view, the strongest possible level of safety
    would be ideal. Of course, the <i>no-throw</i> guarantee is simply
    impossible for many operations, but what about the <i>strong</i> guarantee?
    For example, suppose we wanted atomic behavior for
    <code>vector&lt;T&gt;::insert</code>. Insertion into the middle of a vector
    requires copying elements after the insertion point into later positions,
    to make room for the new element. If copying an element can fail, rolling
    back the operation would require ``undoing'' the previous
    copies...which depends on copying again. If copying back should fail (as it
    likely would), we have failed to meet our guarantee.

    <p>One possible alternative would be to redefine <code>insert</code> to
    build the new array contents in a fresh piece of memory each time, and only
    destroy the old contents when that has succeeded. Unfortunately, there is a
    non-trivial cost if this approach is followed: insertions near the end of a
    vector which might have previously caused only a few copies would now cause
    every element to be copied. The <i>basic</i> guarantee is a
    ``natural'' level of safety for this operation, which it can
    provide without violating its performance guarantees. In fact all of the
    operations in the library appear to have such a ``natural'' level
    of safety.

    <p>Because performance requirements were already a well-established part of
    the draft standard and because performance is a primary goal of the STL,
    there was no attempt to specify more safety than could be provided within
    those requirements. Although not all of the library gives the <i>strong</i>
    guarantee, almost any operation on a standard container which gives the
    <i>basic</i> guarantee can be made <i>strong</i> using the ``make a
    new copy'' strategy described above:

    <blockquote>
<pre>template &lt;class Container, class BasicOp&gt; 
void MakeOperationStrong( Container&amp; c, const BasicOp&amp; op ) 
{ 
    Container tmp(c); // Copy c 
    op(tmp); // Work on the copy 
    c.swap(tmp); // Cannot fail<a title=
"Associative containers whose Compare object might throw an exception when copied cannot use this technique, since the swap function might fail."
 href=
"#footnote7"><sup>7</sup></a>
}
</pre>
    </blockquote>

    <p>This technique can be folded into a wrapper class to make a similar
    container which provides stronger guarantees (and different performance
    characteristics).<a title=
    "This suggests another potential use for the oft-wished-for but as yet unseen container traits&lt;&gt; template: automated container selection to meet exceptionsafety constraints."
     href=
    "#footnote8"><sup>8</sup></a>

    <h2>6 Should we take everything we can get?</h2>

    <p>By considering a particular implementation, we can hope to discern a
    natural level of safety. The danger in using this to establish requirements
    for a component is that the implementation might be restricted. If someone
    should come up with a more-efficient implementation which we'd like to use,
    we may find that it's incompatible with our exception-safety requirements.
    One might expect this to be of no concern in the well-explored domains of
    data structures and algorithms covered by the STL, but even there, advances
    are being made. A good example is the recent <i>introsort</i> algorithm <a
    title=
    "D. R. Musser, ``Introspective Sorting and Selection Algorithms'', Software-Practice and Experience 27(8):983-993, 1997."
     href=
    "#reference6"><sup>[6]</sup></a>,
    which represents a substantial improvement in worst-case complexity over
    the well-established <i>quicksort</i>.

    <p>To determine exactly how much to demand of the standard components, I
    looked at a typical real-world scenario. The chosen test case was a
    ``composite container.'' Such a container, built of two or more
    standard container components, is not only commonly needed, but serves as a
    simple representative case for maintaining invariants in larger systems:

    <blockquote>
<pre>// SearchableStack - A stack which can be efficiently searched 
// for any value. 
template &lt;class T&gt; 
class SearchableStack 
{ 
 public: 
    void push(const T&amp; t); // O(log n) 
    void pop(); // O(log n) 
    bool contains(const T&amp; t) const; // O(log n) 
    const T&amp; top() const; // O(1) 
 private: 
    std::set&lt;T&gt; set_impl; 
    std::list&lt;std::set&lt;T&gt;::iterator&gt; list_impl; 
}; 
</pre>
    </blockquote>

    <p>The idea is that the list acts as a stack of set iterators: every
    element goes into the set first, and the resulting position is pushed onto
    the list. The invariant is straightforward: the set and the list should
    always have the same number of elements, and every element of the set
    should be referenced by an element of the list. The following
    implementation of the push function is designed to give the <i>strong</i>
    guarantee within the natural levels of safety provided by set and list:

    <blockquote>
<pre>template &lt;class T&gt;                                // 1 
void SearchableStack&lt;T&gt;::push(const T&amp; t)         // 2 
{                                                       // 3 
    set&lt;T&gt;::iterator i = set_impl.insert(t);      // 4 
    try                                                 // 5 
    {                                                   // 6 
        list_impl.push_back(i);                         // 7 
    }                                                   // 8 
    catch(...)                                          // 9 
    {                                                   // 10 
        set_impl.erase(i);                              // 11 
        throw;                                          // 12 
    }                                                   // 13 
}                                                       // 14 
</pre>
    </blockquote>

    <p>What does our code actually require of the library? We need to examine
    the lines where non-const operations occur:

    <ul>
      <li>Line 4: if the insertion fails but <code>set_impl</code> is modified
      in the process, our invariant is violated. We need to be able to rely on
      the <i>strong</i> guarantee from <code>set&lt;T&gt;::insert</code>.

      <li>Line 7: likewise, if <code>push_back</code> fails, but
      <code>list_impl</code> is modified in the process, our invariant is
      violated, so we need to be able to rely on the <i>strong</i> guarantee
      from list&lt;T&gt;::insert.

      <li>Line 11: here we are ``rolling back'' the insertion on line
      4. If this operation should fail, we will be unable to restore our
      invariant. We absolutely depend on the <i>no-throw</i> guarantee from
      <code>set&lt;T&gt;::erase</code>.<a title=
      "One might be tempted to surround the erase operation with a try/catch block to reduce the requirements on set&lt;T&gt; and the problems that arise in case of an exception, but in the end that just begs the question. First, erase just failed and in this case there are no viable alternative ways to produce the necessary result. Second and more generally, because of the variability of its type parameters a generic component can seldom be assured that any alternatives will succeed."
       href=
      "#footnote9"><sup>9</sup></a>

      <li>Line 11: for the same reasons, we also depend on being able to pass
      the <code>i</code> to the <code>erase</code> function: we need the
      <i>no-throw</i> guarantee from the copy constructor of
      <code>set&lt;T&gt;::iterator</code>.
    </ul>

    <p>I learned a great deal by approaching the question this way during
    standardization. First, the guarantee specified for the composite container
    actually depends on stronger guarantees from its components (the
    <i>no-throw</i> guarantees in line 11). Also, I took advantage of all of
    the natural level of safety to implement this simple example. Finally, the
    analysis revealed a requirement on iterators which I had previously
    overlooked when operations were considered on their own. The conclusion was
    that we should provide as much of the natural level of safety as possible.
    Faster but less-safe implementations could always be provided as extensions
    to the standard components. <sup><a title=
    "The prevalent philosophy in the design of STL was that functionality that wasn't essential to all uses should be left out in favor of efficiency, as long as that functionality could be obtained when needed by adapting the base components. This departs from that philosophy, but it would be difficult or impossible to obtain even the basic guarantee by adapting a base component that doesn't already have it."
     name="#footnote10">10</a></sup>

    <h2>7 Automated testing for exception-safety</h2>

    <p>As part of the standardization process, I produced an exception-safe
    reference implementation of the STL. Error-handling code is seldom
    rigorously tested in real life, in part because it is difficult to cause
    error conditions to occur. It is very common to see error-handling code
    which crashes the first time it is executed ...in a shipping product! To
    bolster confidence that the implementation actually worked as advertised, I
    designed an automated test suite, based on an exhaustive technique due to
    my colleague Matt Arnold.

    <p>The test program started with the basics: reinforcement and
    instrumentation, especially of the global operators <code>new</code> and
    <code>delete</code>.<sup><a title=
    "An excellent discussion on how to fortify memory subsystems can be found in: Steve Maguire, Writing Solid Code, Microsoft Press, Redmond, WA, 1993, ISBN 1-55615- 551-4."
     name="#footnote11">11</a></sup>Instances of the components (containers and
    algorithms) were created, with type parameters chosen to reveal as many
    potential problems as possible. For example, all type parameters were given
    a pointer to heap-allocated memory, so that leaking a contained object
    would be detected as a memory leak.

    <p>Finally, a scheme was designed that could cause an operation to throw an
    exception at each possible point of failure. At the beginning of every
    client-supplied operation which is allowed to throw an exception, a call to
    <code>ThisCanThrow</code> was added. A call to <code>ThisCanThrow</code>
    also had to be added everywhere that the generic operation being tested
    might throw an exception, for example in the global operator
    <code>new</code>, for which an instrumented replacement was supplied.

    <blockquote>
<pre>// Use this as a type parameter, e.g. vector&lt;TestClass&gt; 
struct TestClass 
{ 
    TestClass( int v = 0 ) 
        : p( ThisCanThrow(), new int( v ) ) {} 
    TestClass( const TestClass&amp; rhs ) 
        : p( ThisCanThrow(), new int( *rhs.p ) ) {} 
    const TestClass&amp; operator=( const TestClass&amp; rhs ) 
        { ThisCanThrow(); *p = *rhs.p; } 
    bool operator==( const TestClass&amp; rhs ) const
        { ThisCanThrow(); return *p == *rhs.p; } 
    ...etc... 
    ~TestClass() { delete p; } 
};
</pre>
    </blockquote>

    <p><code>ThisCanThrow</code> simply decrements a ``throw
    counter'' and, if it has reached zero, throws an exception. Each test
    takes a form which begins the counter at successively higher values in an
    outer loop and repeatedly attempts to complete the operation being tested.
    The result is that the operation throws an exception at each successive
    step along its execution path that can possibly fail. For example, here is
    a simplified version of the function used to test the <i>strong</i>
    guarantee: <a title=
    "Note that this technique requires that the operation being tested be exception-neutral. If the operation ever tries to recover from an exception and proceed, the throw counter will be negative, and subsequent operations that might fail will not be tested for exception-safety."
     href=
    "#footnote12"><sup>12</sup></a>

    <blockquote>
<pre>extern int gThrowCounter; // The throw counter
void ThisCanThrow() 
{ 
    if (gThrowCounter-- == 0) 
        throw 0; 
} 
 
template &lt;class Value, class Operation&gt; 
void StrongCheck(const Value&amp; v, const Operation&amp; op) 
{ 
    bool succeeded = false; 
    for (long nextThrowCount = 0; !succeeded; ++nextThrowCount) 
    { 
        Value duplicate = v; 
        try 
        { 
            gThrowCounter = nextThrowCount; 
            op( duplicate ); // Try the operation 
            succeeded = true; 
        } 
        catch(...) // Catch all exceptions 
        { 
            bool unchanged = duplicate == v; // Test <i>strong</i> guarantee 
            assert( unchanged ); 
        } 
        // Specialize as desired for each container type, to check 
        // integrity. For example, size() == distance(begin(),end()) 
        CheckInvariant(v); // Check any invariant 
    } 
}
</pre>
    </blockquote>

    <p>Notably, this kind of testing is much easier and less intrusive with a
    generic component than with non-generics, because testing-specific type
    parameters can be used without modifying the source code of the component
    being tested. Also, generic functions like <code>StrongCheck</code> above
    were instrumental in performing the tests on a wide range of values and
    operations.

    <h2>8 Further Reading</h2>
    To my knowledge, there are currently only two descriptions of STL
    exception-safety available. The original specification <a title=
    "D. Abrahams, Exception Safety in STLport" href=
    "#reference2"><sup>[2]</sup></a>
    for the reference exception-safe implementation of the STL is an informal
    specification, simple and self-explanatory (also verbose), and uses the
    <i>basic-</i> and <i>strong-</i>guarantee distinctions outlined in this
    article. It explicitly forbids leaks, and differs substantively from the
    final C++ standard in the guarantees it makes, though they are largely
    identical. I hope to produce an updated version of this document soon. 

    <p>The description of exception-safety in the C++ Standard <a title=
    "International Standard ISO/IEC 14882, Information Technology-Programming Languages-C++, Document Number ISO/IEC 14882-1998"
     href=
    "#reference1"><sup>[1]</sup></a>
    is only slightly more formal, but relies on hard-to-read
    ``standardese'' and an occasionally subtle web of implication.<a
    title=
    "The changes to the draft standard which introduced exception-safety were made late in the process, when amendments were likely to be rejected solely on the basis of the number of altered words. Unfortunately, the result compromises clarity somewhat in favor of brevity. Greg Colvin was responsible for the clever language-lawyering needed to minimize the extent of these changes."
     href=
    "#footnote13"><sup>13</sup></a>
    In particular, leaks are not treated directly at all. It does have the
    advantage that it <i>is</i> the standard.

    <p>The original reference implementation <a title=
    "B. Fomitchev, Adapted SGI STL Version 1.0, with exception handling code by D. Abrahams"
     href=
    "#reference5"><sup>[5]</sup></a>
    of the exception-safe STL is an adaptation of an old version of the SGI
    STL, designed for C++ compilers with limited features. Although it is not a
    complete STL implementation, the code may be easier to read, and it
    illustrates a useful base-class technique for eliminating
    exception-handling code in constructors. The full test suite <a title=
    "D. Abrahams and B. Fomitchev, Exception Handling Test Suite" href=
    "#reference3"><sup>[3]</sup></a>
    used to validate the reference implementation has been used successfully to
    validate all recent versions of the SGI STL, and has been adapted to test
    one other vendor's implementation (which failed). As noted on the
    documentation page, it also seems to have the power to reveal hidden
    compiler bugs, particularly where optimizers interact with
    exception-handling code.

    <h2>References</h2>

    <ol>
      <li><a name="reference1">International</a> Standard ISO/IEC 14882,
      <i>Information Technology-Programming Languages-C++</i>, Document Number
      ISO/IEC 14882-1998, available from <a href=
      "http://webstore.ansi.org/ansidocstore/default.asp">http://webstore.ansi.org/ansidocstore/default.asp</a>.

      <li><a name="reference2">D.</a> Abrahams, <i>Exception Safety in
      STLport</i>, available at <a href=
      "http://www.stlport.org/doc/exception_safety.html">http://www.stlport.org/doc/exception_safety.html</a>.

      <li><a name="reference3">D.</a> Abrahams and B. Fomitchev, <i>Exception
      Handling Test Suite</i>, available at <a href=
      "http://www.stlport.org/doc/eh_testsuite.html">http://www.stlport.org/doc/eh_testsuite.html</a>.

      <li><a name="reference4">Tom</a> Cargill, ``Exception Handling:
      A False Sense of Security,'' C++ Report, Nov-Dec 1994, also
      available at <a href=
      "http://www.awprofessional.com/content/images/020163371x/supplements/Exception_Handling_Article.html">http://www.awprofessional.com/content/images/020163371x/supplements/Exception_Handling_Article.html</a>.

      <li><a name="reference5">B.</a> Fomitchev, <i>Adapted SGI STL Version
      1.0</i>, with exception handling code by D. Abrahams, available at <a
      href="http://www.stlport.org">http://www.stlport.org</a>.

      <li><a name="reference6">D.</a> R. Musser, ``Introspective Sorting
      and Selection Algorithms,'' <i>Software-Practice and Experience</i>
      27(8):983-993, 1997.

      <li><a name="reference7">Bjarne</a> Stroustrup, <i>The Design And
      Evolution of C++</i>. Addison Wesley, Reading, MA, 1995, ISBN
      0-201-54330-3, Section 16.9.1.
    </ol>

    <h2>Footnotes</h2>

    <p><a name="footnote1">1</a> Probably the greatest impediment to a solution
    in Cargill's case was an unfortunate combination of choices on his part:
    the interface he chose for his container was incompatible with his
    particular demands for safety. By changing either one he might have solved
    the problem.

    <p><a name="footnote2">2</a> It is usually inadvisable to throw an
    exception from a destructor in C++, since the destructor may itself be
    called during the stack-unwinding caused by another exception. If the
    second exception is allowed to propagate beyond the destructor, the program
    is immediately terminated.

    <p><a name="footnote3">3</a> In practice of course, this function would
    make an extremely poor random sequence generator!

    <p><a name="footnote4">4</a> It is worth noting that mutating algorithms
    usually cannot provide the <i>strong</i> guarantee: to roll back a modified
    element of a range, it must be set back to its previous value using
    <code>operator=</code>, which itself might throw. In the C++ standard
    library, there are a few exceptions to this rule, whose rollback behavior
    consists only of destruction: <code>uninitialized_copy</code>,
    <code>uninitialized_fill</code>, and <code>uninitialized_fill_n</code>.

    <p><a name="footnote5">5</a> All type parameters supplied by clients of the
    C++ standard library are required not to throw from their destructors. In
    return, all components of the C++ standard library provide at least the
    <i>basic</i> guarantee.

    <p><a name="footnote6">6</a> Similar arrangements might have been made in
    the C++ standard for many of the mutating algorithms, but were never
    considered due to time constraints on the standardization process.

    <p><a name="footnote7">7</a> Associative containers whose
    <code>Compare</code> object might throw an exception when copied cannot use
    this technique, since the swap function might fail.

    <p><a name="footnote8">8</a> This suggests another potential use for the
    oft-wished-for but as yet unseen <code>container_traits&lt;&gt;</code>
    template: automated container selection to meet exception-safety
    constraints.

    <p><a name="footnote9">9</a> One might be tempted to surround the erase
    operation with a <code>try</code>/<code>catch</code> block to reduce the
    requirements on <code>set&lt;T&gt;</code> and the problems that arise in
    case of an exception, but in the end that just begs the question. First,
    erase just failed and in this case there are no viable alternative ways to
    produce the necessary result. Second and more generally, because of the
    variability of its type parameters a generic component can seldom be
    assured that any alternatives will succeed.

    <p><a name="footnote10">10</a> The prevalent philosophy in the design of
    STL was that functionality that wasn't essential to all uses should be left
    out in favor of efficiency, as long as that functionality could be obtained
    when needed by adapting the base components. This departs from that
    philosophy, but it would be difficult or impossible to obtain even the
    <i>basic</i> guarantee by adapting a base component that doesn't already
    have it.

    <p><a name="footnote11">11</a> An excellent discussion on how to fortify
    memory subsystems can be found in: Steve Maguire, Writing Solid Code,
    Microsoft Press, Redmond, WA, 1993, ISBN 1-55615- 551-4.

    <p><a name="footnote12">12</a> Note that this technique requires that the
    operation being tested be exception-neutral. If the operation ever tries to
    recover from an exception and proceed, the throw counter will be negative,
    and subsequent operations that might fail will not be tested for
    exception-safety.

    <p><a name="footnote13">13</a> The changes to the draft standard which
    introduced exception-safety were made late in the process, when amendments
    were likely to be rejected solely on the basis of the number of altered
    words. Unfortunately, the result compromises clarity somewhat in favor of
    brevity. Greg Colvin was responsible for the clever language-lawyering
    needed to minimize the extent of these changes.