Features


Here Be Dragons

Alan Griffiths

If learning exception safety may be likened to a journey, then Alan Griffiths is an excellent tour guide. He shows us several alternate routes, and he always keeps the trip interesting.


“The use of animals in maps was commonplace from the earliest times. Man’s deepest fears and superstitions concerning wide expanses of uncharted seas and vast tracts of ’terra incognita’ were reflected in the monstrous animals that have appeared on maps ever since the Mappa Mundi.” — Roderick Barron in Decorative Maps

For many developers, C++ exception handling is like this — a Dark Continent with poor maps and rumors of ferocious beasts. It needn’t be this way; if you’ll come with me, I’ll be your guide to the landmarks and fauna of this region.

In order to discuss exception safety, we need to cover a lot of territory. The next section identifies the “exception safe” mountains in the distance. We’ll be using them as landmarks on our travels — if you don’t take the time to get your bearings now, you’ll end up in the wastelands.

Once you’ve learned the landmarks, I’ll show you a well-trodden path that leads straight towards the highest peak and straight through a tar pit. From experience, I’ve concluded that everyone has to go down this way once, so I’ll go with you to make sure you come back. Not everyone comes back; some give up on the journey, and others press on deeper and deeper into the tar pit until they sink from sight.

During our journey, I’ll tell you the history of how the experts searched for a long time before they discovered a route that bypasses that tar pit and other obstacles. Most maps don’t show this route yet, but I’ll show you the signs to look out for. I’ll also show you that the beasts are friendly and how to enlist their aid.

Now look into the distance and you’ll see two peaks — these are heights of exception safety and are our final destination.

The Mountains (Landmarks of Exception Safety)

The difficulty in writing exception-safe code isn’t in writing the code that throws an exception, or in writing the code that catches the exception to handle it. There are many sources that cover these basics. I’m going to address the greater challenge of writing the code that lies in between the two.

Imagine for a moment the call stack of a running program: function a has called function b, b has called c, and so on, until we reach x. Function x encounters a problem and throws an exception. This exception causes the stack to unwind, deleting automatic variables along the way, until the exception is caught and dealt with by a.

I’m not going to spend any time on how to write functions a or x. I’m sure that the author of x has a perfectly good reason for throwing an exception (running out of memory, disc storage, or whatever) and that the author of a knows just what to do about it (display: “Sorry, please upgrade your computer and try again!”).

The difficult problem lies in writing all the intervening functions in a way that ensures that something sensible happens as a result of this process. If we can achieve this, we have “exception-safe” code. Of course, that begs the question, “what is something sensible?” To answer this, let us consider a typical function f in the middle of the call stack. How should f behave?

Well, if f were to deal with the exception, it might be reasonable for it to complete its task by another method (a different algorithm, or returning a “failed” status code). However, we are assuming the exception won’t be fully handled until we reach a. (It may be caught and rethrown — possibly after being “translated” to a different exception.) Since f doesn’t run to completion, the least we might reasonably expect is the basic exception safety guarantee:

  1. f doesn’t complete its task; but
  2. if f has opened a file, acquired a lock on a mutex, or otherwise “allocated a resource,” then the resource should not leak. (The file must be closed, the mutex must be unlocked, etc.); and
  3. if f changes a data structure, then that structure should remain useable — e.g., no dangling pointers.

These conditions — the basic exception safety guarantee — identify the first and smaller of our landmark mountains. Take a good look at it so that you’ll recognize it later.

The basic exception safety guarantee ensures that if f updates the system state, then the state must remain usable. Note that usable isn’t quite the same as correct. For example, if f were the assignment operator of an address class, part of the address may have been changed, leaving a usable address object containing an incorrect address.

If you are new to exceptions, the basic exception safety guarantee may seem daunting. But not only will we reach this in our travels, we will be reaching an even higher peak called the strong exception safety guarantee. The strong exception safety guarantee places a more demanding constraint on f (one that implies all the others):

Note that it is impossible to implement f to deliver either the basic or strong exception safety guarantees if the behavior in the presence of exceptions of the functions it calls isn’t known, mainly because of possible side effects. This is particularly relevant when the client of f (that is e) supplies the functions to be called either as callbacks, as implementations of virtual member functions, or via template parameters. In such cases, the only recourse is to document the constraints on them — as, for example, the standard library does for types supplied as template parameters to the containers.

If we assume a design with fully encapsulated data, then each function need only be held directly responsible for aspects of the object of which it is a member. For the rest, the code in each function must rely on the functions it calls to behave as documented. (We have to rely on documentation in this case, since in C++ there is no way to express these constraints in the code.)

As we proceed, we’ll find frequent outcrops of the rock that supports our mountains, which is named after the area in which it is found and is called the no-throw exception safety guarantee — as the name suggests, this implies that the corresponding function will never propagate an exception. Clearly operations on the fundamental types provide this guarantee, as will any sequence of no-throw operations. If it were not for the firm footing that these outcrops of the no-throw exception safety guarantee provide, we would have great difficulty ascending the heights.

Before we continue, we’ll rest here a while, and I’ll tell you a little of the history of this landscape. Remember our landmarks and the rock that comes from the area — we’ll soon be making use of them on our journey.

A History of This Territory

The C++ people first came to visit the land of exceptions around 1990 when Margaret Ellis and Bjarne Stroustrup published the Annotated Reference Manual [1]. Under the heading “Experimental Features,” this described the basic mechanisms of exceptions in the language. In this early bestiary, there is an early description of one of the friendly beasts we shall be meeting later on: it goes by the strange name of Resource-Acquisition-Is-Initialization.

By the time the ISO C++ Standards committee circulated Committee Draft 1 in early 1995, C++ people were truly living in exception land. They hadn’t really mapped the territory or produced an accurate bestiary, but they were committed to staying and it was expected that maps and bestiaries would soon be available.

By late 1996 when Committee Draft 2 was circulated, however, the difficulties of this undertaking had become apparent. Around this time there came a number of reports from individual explorers. For example: Dave Abrahams identified the mountains we are using as landmarks in his paper “Exception Safety in STLPort” [2], although the basic exception safety guarantee was originally dubbed the “weak exception safety guarantee.”

Some other studies of the region were produced by H. Muller [3] and Herb Sutter [4, 5]. A little later came a sighting of another of the friendly beasts that we will meet soon, called Acquisition-Before-Release. This beast was first known by a subspecies named Copy-Before-Release and was identified by Kevlin Henney [6]; the subspecies is distinguished by the resources allocated being copies of dynamic objects.

By the time the ISO C++ Language Standard was published in 1998, the main tracks through the territory had been charted. In particular there are clauses in the Standard guaranteeing the behavior of the standard library functions in the presence of exceptions. Also, in a number of key places within the Standard, special mention is made of another friendly beast — the Swap algorithm in its incarnation as the std::swap template function. We will be examining Swap after our detour through the tar pit.

Since the publication of the ISO Standard, more modern charts have been produced, such as by the author in an early version of this article [7]. Bjarne Stroustrup [8] and Dave Abrahams [9] follow a similar route in their examinations of exception safety in the standard library. Herb Sutter [10] takes a different route, but the same landmarks are clearly seen; this is also the first reference to the no-throw exception safety guarantee (although it is clear that earlier explorers knew and exploited it).

Okay, that’s enough rest, we will now take the obvious path and head directly towards the strong exception safety guarantee.

The Tar Pit

It is time to consider an example function, and for this part of the journey, I have chosen the assignment operator for the following class:

class PartOne     { /* omitted */ };
class PartTwo     { /* omitted */ };

class Whole
{
public:
   // ...Lots omitted...

   Whole& operator=(const Whole& rhs);
    
private:
   PartOne*    p1;
   PartTwo*    p2;
};

Those of you that have lived in the old country will know the classical form for the assignment operator. It looks something like the following:

Whole& 
Whole::operator=(const Whole& rhs)
{
   if (&rhs != this)
   {
      delete p1;
      delete p2;
      p1 = new PartOne(*rhs.p1);
      p2 = new PartTwo(*rhs.p2);
   }
   return *this;
}

If you’ve not seen this before, don’t worry because in the new land, it is not safe. Either of the new expressions could reasonably throw (since at the very least they attempt to allocate memory), and this would leave the p1 and p2 pointers dangling. In theory the delete expressions could also throw — but in this article, we will assume that destructors and deallocation functions make the no-throw exception safety guarantee (see the sidebar, “Destructors That Throw Exceptions”).

The obvious solution to the problems caused by an exception being propagated is to catch the exception and do some cleanup before throwing it again. After doing the obvious we have:

Whole&
Whole::operator=(const Whole& rhs)
{
   if (&rhs != this)
   {
      PartOne* t1 = 
         new PartOne(*rhs.p1);

      try
      {
         PartTwo* t2 = 
            new PartTwo(*rhs.p2);

         delete p1;
         delete p2;

         p1 = t1;
         p2 = t2;
      }
      catch (...)
      {
         delete t1;
         throw;
      }
   }
   return *this;
}

Let’s examine why this works:

  1. An exception from the first new expression isn’t a problem — we haven’t yet allocated any resources or modified anything, and the new expression provides the strong guarantee.
  2. If an exception is propagated from the second new expression, we need to release t1. So we catch the exception, delete t1, and rethrow the exception to let it propagate.
  3. Because we are assuming that destructors and operator delete don’t throw, we can pass over the two delete expressions without incident. Similarly the two assignments are of built-in types (pointers), so they cannot throw an exception.
  4. The state of Whole isn’t altered until we’ve done all the things that might throw an exception.

If you peer carefully through the undergrowth, you can see the first of the friendly beasts. This one is called Acquisition-Before-Release. It is recognized because the code is organized so that new resources (the new PartOne and PartTwo) are successfully acquired before the old ones are released.

We’ve achieved the strong exception safety guarantee on our first attempt! But there is some black sticky stuff on our boots.

Tar!

There are problems lying just beneath the surface of this solution. I chose an example that would enable us to pass over the tar pit without sinking too deep. Despite this, we’ve incurred costs: the line count has doubled, and it takes a lot more effort to understand the code well enough to decide that it works.

If you want to, you may take some time out to convince yourself of the existence of the tar pit — I’ll wait. Try the analogous example with three pointers to parts or replace the pointers with two objects contained by a value whose assignment operators may throw exceptions. With real life examples, things get very messy very quickly. (If you want a guide to the latter example — the “Cargill Widget Example” — I can recommend Herb Sutter [11].)

Many people have reached this point and become discouraged. I agree with them: routinely writing code this way is not reasonable. Too much effort is expended on exception safety housekeeping chores like releasing resources. If you hear that “writing exception safe code is hard” or that “all those try...catch blocks take up too much space,” you are listening to someone who has discovered the tar pit.

I’m now going to show you how exception handling allows you to use less code (not more), and I’m not going to use a single try...catch block for the rest of the article! (In a real program, the exception must be caught somewhere — like function a in the discussion above — but most functions simply need to let the exceptions pass through safely.)

The Royal Road

There are three “golden rules:”

  1. Destructors (and functions that release resources, such as memory) may not propagate exceptions.
  2. Operations to swap the states of two instances of a class make the no-throw exception safety guarantee.
  3. An object may own at most one resource.

We’ve already met the first rule.

The second rule isn’t obvious, but you’ll see that it provides us a firm footing during the ascent. The idea is that exchanging the states of two objects that own resources is feasible without the need to allocate additional resources. Since nothing needs to be allocated, failure needn’t be an option, and consequently there is no need to throw an exception. (It is worth mentioning that the no-throw guarantee is usually not feasible for assignment, which may have to allocate resources.)

If you look at the ISO C++ Language Standard, you’ll find that std::swap provides the no-throw guarantee for fundamental types and for relevant types in the standard library. This is achieved by overloading std::swap — there is a template corresponding to each of the STL containers. This looks like a good way to approach Swap, but introducing additional overloads of std::swap is not permitted by the language standard. The Standard does permit explicit specialization of an existing std::swap template function on user-defined classes, and this is what I would recommend doing where applicable (there is an example below). The standards committee is currently considering a defect report [12] that addresses the problem caused by these rules for the authors of user-defined template classes.

The third rule addresses the cause of all the messy exception-handling code we saw in the last section. It was because creating a new second part might fail that we wrote code to handle it and doubled the number of lines in the assignment operator.

We’ll now revisit the last example and make use of the above rules. In order to conform to the rule regarding ownership of multiple objects, we’ll delegate the responsibility of resource ownership to a couple of helper classes. I’m using the std::auto_ptr<> template to generate the helper classes here because it is standard, not because it is the ideal choice.

class Whole {
public:
   // ...Lots omitted...

   Whole& operator=(const Whole& rhs);

private:
   std::auto_ptr<PartOne>    p1;
   std::auto_ptr<PartTwo>    p2;
};

Whole& 
Whole::operator=(const Whole& rhs)
{
   std::auto_ptr<PartOne> 
      t1(new PartOne(*rhs.p1));
   std::auto_ptr<PartTwo> 
      t2(new PartTwo(*rhs.p2));

   p1 = t1;
   p2 = t2;

   return *this;
}

Not only is this shorter than the original exception-unsafe example, it meets the strong exception safety guarantee.

Look at why it works:

  1. There are no leaks: whether the function exits normally or via an exception, t1 and t2 will delete the parts they currently own.
  2. The assignment expressions cannot throw (first rule).
  3. The state of the Whole isn’t altered until we’ve done all the things that might throw an exception.

Oh, by the way, I’ve not forgotten about self-assignment. Think about it — you will see the code works without a test for self-assignment. Such a test may be a bad idea: assuming that self-assignment is very rare in real code and that the branch could have a significant cost, Francis Glassborow suggested a similar style of assignment operator as a speed optimization [13]. Following on from this, Kevlin Henney explored its exception safety aspects in [14] and [6].

We are on much firmer ground than before: it isn’t hard to see why the code works and generalizing it is simple. You should be able to see how to manage a Whole with three auto_ptrs to Parts without breaking stride.

You can also see another of the friendly beasts for the first time. Putting the allocation of a resource (here a new expression) into the initializer of a manager object (e.g., auto_ptr<PartOne>) that will delete it on destruction is Resource-Acquisition-Is-Initialization. And, of course, we can once again see Acquisition-Before-Release.

The Assignment Operator — A Special Case

Before I go on to deal with having members that may throw when updated, I’ve a confession I need to make. It is possible, and usual, to write the assignment operator more simply than the way I’ve just demonstrated. The above method is more general than what follows and can be applied when only some aspects of the state are being modified. The canonical form of an assignment operator is:

Whole& Whole::operator=(const Whole& rhs)
{
    Whole copy(rhs);
    swap(copy);
    return *this;
}

Remember the second rule: Whole is a good citizen and provides for Swap (by supplying the swap member function). I also make use of the copy constructor — but it would be a perverse class design that supported copy assignment but not copy construction. I’m not sure whether the zoologists have determined the relationship between Swap and copying here, but the traveler won’t go far wrong in considering Copy-And-Swap as a species in it own right.

For completeness, I’ll show the member functions used above:

Whole::Whole(const Whole& rhs)
:  p1(new PartOne(*rhs.p1)),
   p2(new PartTwo(*rhs.p2))
{
}

void Whole::swap(Whole& that)
{
   std::auto_ptr<PartOne> t1(that.p1);
   that.p1 = p1;
   p1 = t1;
   std::auto_ptr<PartTwo> t2(that.p2);
   that.p2 = p2;
   p2 = t2;
}

Note that one must resist the temptation to write std::swap(p1, that.p1) as I did in earlier articles because it isn’t guaranteed to work by the language standard. (auto_ptr doesn’t have normal copy or assignment semantics — thanks are due to Herb Sutter who pointed this problem out.)

One further point about making Whole a good citizen is that we need to specialize std::swap to work through the swap member function. By default, std::swap will use assignment — and not deliver the no-throw guarantee we need for Swap. The Standard allows us to specialize existing names in the std namespace on our own types, and it is good practice to do so in the header that defines the type.

namespace std
{
   template<>
   inline void swap(cuj::Whole& lhs, cuj::Whole& rhs)
   {
      lhs.swap(rhs);
   }
}

This avoids any unpleasant surprises for client code that attempts to std::swap two Wholes.

Although we’ve focused on attaining the higher peak of strong exception safety guarantee, we’ve actually covered all the essential techniques for achieving either strong or basic exception safety. The remainder of the article shows the same techniques being employed in a more complex example and gives some indication of the reasons you might choose to approach the lesser altitudes of basic exception safety.

In Bad Weather

We can’t always rely on bright sunshine, or on member variables that are as easy to manipulate as pointers. Sometimes we have to deal with rain and snow, or base classes and member variables with an internal state.

To introduce a more complicated example, I’m going to elaborate the Whole class we’ve just developed by adding member functions that update p1 and p2. Then I’ll derive an ExtendedWhole class from it that also contains an instance of another class: PartThree. We’ll be assuming that operations on PartThree are exception safe, but, for the purposes of discussion, I’ll leave it open whether PartThree offers the basic or the strong exception safety guarantee.

Whole& Whole::setP1(const PartOne& value)
{
   p1.reset(new PartOne(value));
   return *this;
}

Whole& Whole::setP2(const PartTwo& value)
{
   p2.reset(new PartTwo(value));
   return *this;
}

class ExtendedWhole : private Whole
{
public:

   // Omitted constructors & assignment

   void swap(const ExtendedWhole& rhs);

   void setParts(
      const PartOne& p1,
      const PartTwo& p2,
      const PartThree& p3);

private:
   int       count;
   PartThree body;
};

The examples we’ve looked at so far are a sufficient guide to writing the constructors and assignment operators. We are going to focus on two member functions: the swap member function and a setParts member function that updates the parts.

Writing swap looks pretty easy — we just swap the base class and each of the members. Since each of these operations is “no-throw,” the combination of them is also “no-throw.”

void ExtendedWhole::swap(ExtendedWhole& rhs)
{
   Whole::swap(rhs);
   std::swap(count, rhs.count);
   std::swap(body,  rhs.body);
}

Writing setParts looks equally easy: Whole provides member functions for setting p1 and p2, and we have access to body to set that. Each of these operations is exception safe; indeed the only one that need not make the strong exception safety guarantee is the assignment to body. Think about it for a moment: is this version of setParts exception safe? And does it matter if the assignment to body offers the basic or strong guarantee?

void ExtendedWhole::setParts(
   const PartOne& p1,
   const PartTwo& p2,
   const PartThree& p3)
{
   setP1(p1);
   setP2(p2);
   body = p3;
}

Let’s go through it together: none of the operations leaks resources, and setParts doesn’t allocate any resources, so we don’t have any leaks. If an exception propagates from any of the operations, then they leave the corresponding subobject in a useable state, and presumably that leaves ExtendedWhole useable. (It is possible, but in this context implausible, to construct examples where this isn’t true.) However, if an exception propagates from setP2 or from the assignment, then the system state has been changed. And this is so regardless of which guarantee PartThree makes.

The easy way to support the strong exception safety guarantee is to ensure that nothing is updated until we’ve executed all the steps that might throw an exception. The simple approach is to copy the object, make changes to the copy, and swap, as shown below:

void ExtendedWhole::setParts(
   const PartOne& p1,
   const PartTwo& p2,
   const PartThree& p3)
{
   ExtendedWhole copy(*this);

   copy.setP1(p1).setP2(p2);
   copy.body = p3;

   swap(copy);
}

Sadly this is often impractical — perhaps because the object doesn’t have value semantics (i.e., no copy or assignment). This means taking copies of individual sub-objects and making the changes on the copies, prior to swapping the state between the copies and the original sub-objects:

void ExtendedWhole::setParts(
   const PartOne& p1,
   const PartTwo& p2,
   const PartThree& p3)
{
   Whole copy(*this);

   copy.setP1(p1).setP2(p2);
   body = p3;

   Whole::swap(copy);
}

Despite its similar appearance, this is harder than the previous example: for example, does it matter if the assignment to body offers the basic or strong guarantee? Yes it does. If it offers the strong guarantee, then all is well with the above; if not, then the assignment needs to be replaced with Copy-And-Swap:

   PartThree(p3).swap(body);

Once again we have attained the highest peak, but this may not be healthy. On terrestrial mountains above a certain height, there is a “death zone” where the supply of oxygen is insufficient to support life. Something similar happens with exception safety: there is a cost to implementing the strong exception safety guarantee. Although the code you write isn’t much more complicated than the “basic” version, additional objects are created and these allocate resources at run time. This causes the program to make more use of resources and to spend time allocating and releasing them.

Trying to remain forever at high altitude will drain the vitality. Fortunately, the basic exception safety guarantee is below the death zone: when we make a composite operation whose parts offer this guarantee, we automatically attain the basic guarantee. (As the first version of setParts shows, this is not true of the strong guarantee.) From the basic guarantee, there is an easy climb from this level to the strong guarantee by means of Copy-And-Swap.

Looking Back

Before we descend from the peak of strong exception safety guarantee and return to our starting point, look back over the route we covered. In the distance, you can see the well-trampled path that led us from our first camp to the tar pit. On this side of the tar pit, there are a few tracks made by determined explorers that lead from the tar pit up a treacherous scree slope to where we stand. Off to the left is the easier ridge path we’ve just ascended that led us by way of basic exception safety guarantee, and beyond the ridge is the road that led us past the tar pit. Fix these landmarks in your mind and remember that the beasts we met are not as fierce as their reputations.

References

[1] Ellis and Stroustrup. The Annotated C++ Reference Manual (Addison-Wesley, 1990).

[2] Dave Abrahams. “Exception Safety in STLPort,” http://www.stlport.org/doc/exception_safety.html.

[3] H. Muller. “Ten Rules for Handling Exception Handling Successfully,” C++ Report, January 1996.

[4] Herb Sutter. “Designing Exception-Safe Generic Containers,” C++ Report, September 1997.

[5] Herb Sutter. “More Exception-Safe Generic Containers,” C++ Report, November/December 1997.

[6] Kevlin Henney. “Creating Stable Assignments,” C++ Report, June 1998.

[7] Alan Griffiths. “The Safe Path to C++ Exceptions,” EXE, December 1999.

[8] Bjarne Stroustrup. The C++ Programming Language, Special Edition (Addison-Wesley, 2000), Appendix E: “Standard Library Exception Safety.” (This appendix appears only in the “Special Edition”, but is available on the web at www.research.att.com/~bs/3rd_safe.pdf.)

[9] Dave Abrahams. “Exception-Safety in Generic Components.” http://people.ne.mediaone.net/abrahams/abrahams.html.

[10] Herb Sutter. Exceptional C++ (Addison Wesley, 2000).

[11] Herb Sutter. “Sutter’s Mill,” C++ Report, March 2000.

[12] Dave Abrahams. “User Supplied Specializations or Overloads of Namespace std Function Templates,” http://anubis.dkuug.dk/JTC1/SC22/WG21/docs/lwg-active.html#226.

[13] Francis Glassborow. “The Problem of Self-Assignment,” Overload 19, ISSN 1354-3172.

[14] Kevlin Henney. “Self Assignment? No Problem!” Overload 20, 21, ISSN 1354-3172.

Alan Griffiths works for Experian Ltd. designing software, writing C++ and Java, and mentoring developers. His website is at www.octopull.demon.co.uk/. He also chairs the Association of C and C++ Users (see http://accu.org/ or email info@accu.org). He can be reached at alan.griffiths@uk.experian.com.