cat head > /dev/www From my head to the web

15Apr/112

Exception unsafe

I was reading up about exception safe code in C++ and the difference between the strong and basic exception guarantees. The article here (http://www.gotw.ca/gotw/082.htm) outlines what it means for code to satisfy the basic exception safety guarantee, however it doesn’t give an example. So I came up with something that is exception unsafe. The code below doesn’t satisfy the basic guarantee because it fails to delete the memory allocated to the pointer.

Exception unsafe.

#include <iostream>
#include <stdexcept>

using namespace std;

void h()
{
  throw logic_error((string("Failure in ") + string(__func__)));
}

void g()
{
  int* pi = new int;
  h();
  delete pi;
}

void f()
{
  try
  {
    g();
  }
  catch (const exception& e)
  {
    cout << e.what() << endl;
  }
}

int main(int argc, char** argv)
{
  f();
}

To ensure that it meets the basic guarantee the exception needs to be caught in g() and the memory deallocated. The code below does this. Unfortunately there is a bit of awkwardness here: note the double delete. One is in the bad path where the exception is caught and the other is in the good code path. It would be nice if the double delete wasn’t necessary. Other languages have a finally statement that is executed in both the good and bad case, however C\++ doesn’t have this but the problem is still solvable as shown below.

Basic exception safety met.

#include <iostream>
#include <stdexcept>

using namespace std;

void h()
{
  throw logic_error((string("Bad logic in ") + string(__func__)));
}

void g()
{
  int* pi = new int;
  try
  {
    h();
  }
  catch (const exception& e)
  {
    cout << "Caught exception: \"" << e.what() << "\" in " << __func__  << endl;
    delete pi;
    throw;
  }
  delete pi;
}

void f()
{
  try
  {
    g();
  }
  catch (const exception& e)
  {
    cout << "Caught exception: \"" << e.what() << "\" in " << __func__  << endl;
  }
}

int main(int argc, char** argv)
{
  f();
}

The lack of a finally statement in C++ isn’t a problem as long as the principle Resource Acquisition is Initialization is applied. It’s fairly straightforward and with a little bit of thought I think that most good programmers would realize what to do here in order to avoid having a double delete. The trick is to write a wrapper class to manage the resource and in the destructor clean up any allocated memory. If you follow this guideline then there is no need for C++ to have a finally statement. This is mentioned in the the C++ FAQ in section 17.6. The following code applies RAII. I’ve found myself doing this in code I’ve writted without explicitly realizing the technique I was applying had a name assoiciated with it.

Basic exception safety with RAII.

#include <iostream>
#include <stdexcept>

using namespace std;

class Resource
{
  private:
    int* pi;

  public:
    Resource()
    {
      cout << __func__ << endl;
      pi = new int;
    }

    ~Resource()
    {
      cout << __func__ << endl;
      delete pi;
    }
};

void h()
{
  throw logic_error((string("Bad logic in ") + string(__func__)));
}

void g()
{
  Resource r;
  h();
}

void f()
{
  try
  {
    g();
  }
  catch (const exception& e)
  {
    cout << "Caught exception: \"" << e.what() << "\" in " << __func__  << endl;
  }
}

int main(int argc, char** argv)
{
  f();
}
Comments (2) Trackbacks (0)
  1. Why not just use a std::auto_ptr to get RAII?

    • Hey Lenny

      Yeah you can (the post doesn’t say not to use std::auto_ptr). The main theme of the post was to show some exception unsafe code and I didn’t want to write more about auto_ptr, unique_ptr due to time constraints.


Leave a comment

(required)

No trackbacks yet.