Re: When to introduce exception safety

From: Chris \( Val \) (chrisval_at_bigpond.com.au)
Date: 02/29/04


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



Relevant Pages

  • Re: performance
    ... efficient language and still have an inefficient system. ... > Even on these super performant silicons, some kernel code and device drivers ... > exceptions. ... > relates to the efficiency/performance of the libraries ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: performance
    ... ;-)) still use plain C code for some kernel stuff and some parts are still ... Even on these super performant silicons, some kernel code and device drivers ... exceptions. ... relates to the efficiency/performance of the libraries ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Setting the FPU Control Word on application startup
    ... floating point exceptions be disabled at all times. ... libraries expect the FPU exceptions to be disabled because the Microsoft ...
    (borland.public.delphi.non-technical)
  • Re: Any non-GNU compilers? sick of GNU copylefts
    ... >> Compiling comercial products statically and selling them is prohibited ... >> with the libs source code and link everything dynamically. ... each of the libraries as well as ... typically use exceptions, so this is the right thing to do. ...
    (comp.unix.bsd.openbsd.misc)
  • Re: Setting the FPU Control Word on application startup
    ... > requires that floating point exceptions be disabled at all times. ... > lot of third-party libraries expect the FPU exceptions to be disabled ... The functions Trunc/Trunc64 both read and set the FPU CW ...
    (borland.public.delphi.non-technical)