46fcf916   
   From: bouncer@dev.null   
      
   Gene Bushuyev wrote:   
      
   > On May 11, 3:45 am, Zeljko Vrba    
   > wrote:   
   >>   
   >> For example: write() or close() of a file failing, getting an error   
   >> while acquiring/releasing a mutex, etc. So instead of writing   
   >>   
   >> close(fd);   
   >>   
   >> and hoping all goes well (and never knowing when something goes   
   >> wrong!), you could write   
   >>   
   >> if (close(fd) < 0) throw std::runtime_error("close()");   
   >>   
   >> Postcondition was (presumably) properly formatted file, but if   
   >> close() has failed, you cannot guarantee that, hence -- throw   
   >> exception.   
   >>   
   >> But this perspective is incompatible with RAII: typically, you   
   >> would wrap a file or mutex in a class whose destructor would   
   >> release the resource, but throwing from a destructor is a big   
   >> no-no. So we have a mechanism for reporting broken invariants   
   >> (exceptions), but it is unusable if proper destruction (e.g.,   
   >> successfully closed file or released mutex) is among them.   
   >   
   > I agree that the errors in destructors is a difficult subject for   
   > which C++ standard doesn't offer any good mechanism to deal with. I   
   > would like to hear the opinions on this subject. What practical ways   
   > do we have to deal with errors in destructors?   
      
   I wonder if that's the right question. Another one would be: what is   
   the reason we need to deal with errors in destructors in the first   
   place?   
      
   For example, consider the call to close() above. The Linux manual   
   page for close() comments:   
      
    Not checking the return value of close() is a common but   
    nevertheless serious programming error. It is quite possible that   
    errors on a previous write(2) operation are first reported at the   
    final close(). Not checking the return value when closing the file   
    may lead to silent loss of data. This can especially be observed   
    with NFS and with disk quota.   
      
   In other words, close() behaves as if it both performs some I/O (which   
   can fail for reasons beyond the control of the program) *and*   
   deallocates a system resource (which should never fail). In the first   
   case, we may want to report an error by throwing an exception; in the   
   second case, we need to fix the program.   
      
   It is close()'s surprising double-duty nature that causes the problem   
   we're discussing here. Destructors are compiler-generated callbacks   
   that tell us to release our resources; they were simply not designed   
   for anything else.   
      
   Obviously, as a practical matter, it is possible to design a class   
   that allows users to explicitly request an exception on I/O errors   
   reported by close():   
      
    class file {   
    public :   
    // constructors, read(), write(), and...   
      
    void close(); // throws on I/O errors   
      
    ~file(); // closes implictly if still open; never throws   
    };   
      
    void save()   
    {   
    file f(/* args */);   
      
    // gather and write data, and then...   
      
    f.close();   
    }   
      
   Here, closing the file will only result in an exception if an I/O   
   error is reported *and* we're not unwinding because of some other   
   exception.   
      
   - Wil   
      
      
   --   
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]   
    [ comp.lang.c++.moderated. First time posters: Do this! ]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|