Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle a file destructor throwing an exception?

Tags:

c++

It's important to detect an error when you close a file to which you were writing, because the last part of your data could be flushed during the close, and if it's lost then your last write effectively failed and someone should know about it. The destructor for a file object is a nice place to automatically close it, but people say not to throw exceptions from destructors, so if the close fails then how do you know about it?

I've heard people recommend calling the close() method of the file manually. That's sounds nice, except what happens if the close() methods of multiple files all fail in a situation like this:

    MyFile x(0), y(1), z(2);
    x.close();
    y.close();
    z.close();

?

Well, it seems that if the close() method of 'x' throws an exception then you've done well to uphold the rule to avoid throwing exceptions in the destructor of 'x', except now you're good-intentioned early calls to the close() methods of 'y' and 'z' won't execute until their destructors. So, then when the close() method of 'y' is called in the destructor of 'y' or the close() method of 'z' is called in the destructor of 'z', and if they do throw exceptions then you're screwed.

Is there a reasonable way to not be screwed in such a situation?

like image 659
Steel Avatar asked Feb 01 '10 15:02

Steel


3 Answers

Yes, catch the exception thrown from close in the destructor.

It is vitally important that a C++ destructor not throw an exception period. To do otherwise will mess up many of the resource management routines within virtually every available library.

True, you lose the failure information in this case by catching the exception. If the user is actually concerned about the error, they can manually call close and deal with the exception.

like image 88
JaredPar Avatar answered Sep 30 '22 12:09

JaredPar


This is a FAQ item: 17.3 How can I handle a destructor that fails?

Edit:

Well, it seems that if the close() method of 'x' throws an exception then you've done well to uphold the rule to avoid throwing exceptions in the destructor of 'x', except now you're good-intentioned early calls to the close() methods of 'y' and 'z' won't execute until their destructors.

No. Dtors for y and z will be called when the stack unwinds provided you have a try-catch block installed around the MyFile ... z.close() part. A better idea would be to put a close in the dtor as well. The dtor for x will not be -- so some cleanup is in order in the catch block.

I suggest you run the following program to better understand dtor-calls in the case of an exception (once as-is and again by uncommenting the S x line):

#include <iostream>

using namespace std;

struct s {
 s(int i = 0) : m_i( i ) { cout << __func__ << endl; }
 ~s() { if (m_i == 0) except(); cout << __func__ << endl; }
 void except() { throw 42; }
 int m_i;
};

int main() {
  try
  {
      s y(2), z(3);
      /* s x */
      y.except();
  }
  catch (...) { cout << "exception\n"; }

  cout << "stack unwound\n";
}
like image 24
dirkgently Avatar answered Sep 30 '22 11:09

dirkgently


You shouldn't throw from a destructor -- so:

If close had calls that threw exceptions, I would swallow them and do one of the following:

Option 1: Write out an error message and kill the program.

Option 2: Make the error available via a wrapper object (I would probably do this) or a global variable or (prefferably) a variable that is in thread local memory.

Option 1 and 2 both seem reasonable here.

With option 2 and the wrapper you would do:

WrapFileX.close();
WrapFileY.close();
WrapFileZ.close();

if(WrapFileX.hasError || WrapFileY.hasError || WrapfileZ.hasError)
{   //log
    exit(1)
}
like image 33
Hassan Syed Avatar answered Sep 30 '22 13:09

Hassan Syed