I have a program that is responsible for reading data, formatting it and creating records, and outputting records to files. The important classes for this discussion are:
FileManager
During a normal process shutdown, the destructors for these classes all get called which causes all remaining records to get flushed out to the current output file and then it gets closed. This ensures we don't lose any data.
However, during an error case, we need to shutdown but we don't want to flush and close the file since the data is likely corrupt. Normally what happens is an exception will get thrown which gets caught in the RecordGenerator
which then decides if this is a fatal error or not. If it is, it will initiate the application shutdown. It's at this point that the FileManager
gets destructed, but needs to know whether there is an error. Likewise, when the FileManager
gets destructed, this causes the OutputFile
to get destructed which also needs to know whether there is an error.
My first reaction was to just add some public functions that set error flags for these classes, so RecordGenerator
could call FileManager::setErrorFlag()
which can then call OutputFile::setErrorFlag()
. Adding a chain of these seems like a pretty bad smell to me, especially if you consider the object chain could be much longer than this.
Is there some better way of handling this sort of scenario?
This is a typical problem when people start using RAII the way it's not meant to be used. Destructors should clean resources and revert whatever they are responsible to. They should not commit changes. Typical exception safe C++ code looks like this:
For example:
X& X::operator = (const X& x)
{
X y(x); // allocate
this->swap(y); // commit
return *this;
}
void f()
{
Transaction t(...); // begin transaction
// operate
t.commit(); // commit transaction
}
void g()
{
File f(...); // open file
// write to file
f.flush(); // flush the buffers, this may throw but not f.~File()
}