Re: When to introduce exception safety
From: Chris \( Val \) (chrisval_at_bigpond.com.au)
Date: 02/29/04
- Next message: Jeff Schwab: "Re: Standard library confusion"
- Previous message: Walkaway Renouf: "Standard library confusion"
- In reply to: Francis Glassborow: "Re: When to introduce exception safety"
- Next in thread: Francis Glassborow: "Re: When to introduce exception safety"
- Reply: Francis Glassborow: "Re: When to introduce exception safety"
- Reply: Francis Glassborow: "Re: When to introduce exception safety"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sun, 29 Feb 2004 23:58:34 +1100
"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:iZ+BybgmXdQAFwOE@robinton.demon.co.uk...
| In message <c1sjee$1lmsdm$1@ID-110726.news.uni-berlin.de>, "Chris ( Val
| )" <chrisval@bigpond.com.au> writes
[snip]
| >Well, remember when you said: "think library implementors" ?
| >
| >- Think of this code being inside of an library that your
| > employer purchased - Now how do you deal with it :-).
|
| If such code is in a library then it the library is unusable in the
| context of exceptions because it lacks exception safety -- well it might
| be usable in a context where resource leaks could be tolerated.
In an ideal world, yes - it is not a very well designed library,
and if possible, should be avoided. However, in the real world,
I'm sure libraries like this exist, and we have to somehow work
around them.
| >| Note that if you do you no longer need to write a dtor.
| >
| >True.
| >
| >| > // More members...
| >| > public:
| >| > FileReader( const char* filename )
| >| > : InFile( new std::ifstream( filename ) )
| >| > {
| >| > InFile -> exceptions( std::ios_base::badbit |
| >| > std::ios_base::failbit );
| >| > }
| >| >
| >| > ~FileReader() { delete InFile; }
| >| > };
| >| >
| >| >In this simple example, the constructor may throw
| >| >different exceptions based on runtime conditions.
| >|
| >| Exactly, and an exception safe style ensures that all sub-objects have
| >| dtors that clean-up. That leads to avoidance of raw pointers for
| >| ownership, but they are fine for association [with objects that have an
| >| independent existence]. If you see a pointer data member in modern style
| >| C++ you expect association not ownership.
| >
| >Yes, but even an destructor can throw, and you would best avoid
| >letting the exception object propagate out of there, and this is
| >why I think the code can become overly complex, because we are
| >not always writing new stand alone code, but new code that may
| >or may not use existing libraries.
|
| If a dtor throws all bets are off. It is not possible to write
| exception safe code in the presence of dtors that can throw.
But how do you guarantee that a given destructor will not throw ?
Can you guarantee that a destructor will not throw ?
| >For all new code that does not rely on third party libraries, then
| >I agree, that exception safety should be considered way before the
| >coding phase.
| >
| >| I know this change in style is hard at first. Back in about 93 when I
| >| first came to work with exceptions I thought I had gone to hell, it was
| >| only after I recognised things such as the above that I realised that I
| >| was being punished for using an inappropriate coding style:-)
| >
| >Well, I do have a good grasp of their use(and I'm not really
| >afraid of using them), but I think they can obfuscate your code
| >to some extent :-).
|
| Which is where we disagree, exceptions decouple normal code from code
| handling problems and so allow both to be written with greater clarity.
Well, I am yet to really see this greater clarity in full swing.
Even in my earlier example - lets modify it slightly now:
class Transaction
{
private:
bool Flag;
std::ofstream Database;
std::ifstream Updater;
std::ofstream Backup;
public:
Transaction( const char* master ) : Flag( false ),
Database( master, std::ios_base::app ),
Updater( "Updater.txt" ), Backup( "Backup.txt" )
{
InFile.exceptions( std::ios_base::badbit | std::ios_base::failbit );
}
~FileReader()
{
if( Flag )
Commit();
else
Rollback();
}
};
There are quite a number of possibilities where each stream
may throw exceptions. Additionally, it has been recommended
(from what I read a while ago), that 'C' library functions
be wrapped up to throw exceptions.
In this case, Commit() and Rollback() will use 'std::remove'
and 'std::rename'. They might look like this, as non-member
functions:
void RemoveFile( const std::string& Filename )
{
if( !std::remove( Filename.c_str() ) == 0 )
throw std::runtime_error( "Could not remove " + Filename + "." );
}
void RenameFile( const std::string& OldName, const std::string& NewName )
{
if( !std::rename( OldName.c_str(), NewName.c_str() ) == 0 )
throw std::runtime_error( "Could not rename " + OldName + "." );
}
Now, lets say that we cannot afford to modify the state of the
database, unless we completely succeed, otherwise, we could
corrupt it. Further more, how can I guarantee that I can effectively
'rollback' the transaction, and leave everything in its original
state ? - including removing the temporary files.
Imagine the updater file came from an PDA syncing up new data
to a server, and an exception was thrown. Even if we cannot
recover, how do we signify to the PDA success or failure, and
reset everything for another(future) attempt ?
Cheers.
Chris Val
- Next message: Jeff Schwab: "Re: Standard library confusion"
- Previous message: Walkaway Renouf: "Standard library confusion"
- In reply to: Francis Glassborow: "Re: When to introduce exception safety"
- Next in thread: Francis Glassborow: "Re: When to introduce exception safety"
- Reply: Francis Glassborow: "Re: When to introduce exception safety"
- Reply: Francis Glassborow: "Re: When to introduce exception safety"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|