RAII and cleanup functions that can throw

Discussion in 'C++' started by Kenneth Porter, Jan 10, 2007.

  1. I've read this article and have some followup questions.

    http://groups.google.com/group/comp.lang.c .moderated/browse_thread/thr
    ead/9d5324ce02f4d89b/

    I'm working on an embedded robotics application that cannot terminate.

    I often have the need to temporarily change some setting before
    performing an operation. If the operation fails, I still need to restore
    the original setting. It's possible for the restore operation to fail
    (eg. a communication fault that temporarily prevents the request from
    reaching the hardware), in which case user intervention is required. The
    application can later re-initialize the system to a known state.

    A typical scenario, written in C (assuming a zero errorcode means no
    error):

    int oldvalue;
    int errorcode = SaveOldValue(&oldvalue);
    if (!errorcode)
    errorcode = SetValue(newvalue);
    if (!errorcode)
    errcode = PerformOperation();
    int errcode2 = SetValue(oldvalue);
    return errcode ? errcode : errcode2;

    This idiom might be nested.

    With C++, it seems like I should report errors via exceptions, and
    perform the temporary settings changes with RAII objects (to insure that
    original settings are restored through all error paths).

    How can I capture the error if the restore operation fails? Do I simply
    use uncaught_exception() in the RAII dtor to throw only if I'm not
    handling an exception from the main operation?
     
    Kenneth Porter, Jan 10, 2007
    #1
    1. Advertising

  2. * Kenneth Porter:
    > I've read this article and have some followup questions.
    >
    > http://groups.google.com/group/comp.lang.c .moderated/browse_thread/thr
    > ead/9d5324ce02f4d89b/
    >
    > I'm working on an embedded robotics application that cannot terminate.
    >
    > I often have the need to temporarily change some setting before
    > performing an operation. If the operation fails, I still need to restore
    > the original setting. It's possible for the restore operation to fail
    > (eg. a communication fault that temporarily prevents the request from
    > reaching the hardware), in which case user intervention is required. The
    > application can later re-initialize the system to a known state.
    >
    > A typical scenario, written in C (assuming a zero errorcode means no
    > error):
    >
    > int oldvalue;
    > int errorcode = SaveOldValue(&oldvalue);
    > if (!errorcode)
    > errorcode = SetValue(newvalue);
    > if (!errorcode)
    > errcode = PerformOperation();
    > int errcode2 = SetValue(oldvalue);
    > return errcode ? errcode : errcode2;
    >
    > This idiom might be nested.
    >
    > With C++, it seems like I should report errors via exceptions, and
    > perform the temporary settings changes with RAII objects (to insure that
    > original settings are restored through all error paths).
    >
    > How can I capture the error if the restore operation fails? Do I simply
    > use uncaught_exception() in the RAII dtor to throw only if I'm not
    > handling an exception from the main operation?


    Note that the C-style code above "solves" the double exception/error
    problem by, in the last line, discarding errcode2 (the SetValue error)
    if any operation before that fails.

    To do /the same/ using C++ exceptions instead of error codes:

    struct ValueUsageWrapper
    {
    int myOriginal;
    bool myShouldReportRestoreError;

    static int current()
    {
    int v;
    if( SaveOldValue( &v ) != 0 ) { throw ValueAccessException(); }
    return v;
    }

    static void set( int v )
    {
    if( SetValue( v ) != 0 ) { throw ValueAccessException(); }
    }

    ValueUsageWrapper()
    : myOriginal( current() )
    , myShouldReportRestoreError( false )
    {}

    ~ValueUsageWrapper()
    {
    try
    {
    set( myOriginal );
    }
    catch( ... )
    {
    if( myShouldReportRestoreError ) { throw; }
    }
    }

    void reportAnyRestoreError() { myShouldReportRestoreError = true; }
    };

    void performOperationOrX()
    {
    if( PerformOperation() != 0 ) { throw SomeThing(); }
    }

    void foo( int newValue )
    {
    ValueUsageWrapper valueUsage;

    valueUsage.set( newValue );
    performOperationOrX();
    valueUsage.reportAnyRestoreError();
    }

    But again, note that by design -- yours!, the C-style code ;-) --
    the client code will not be aware of failure to restore the value if any
    operation before that fails. This is discussed in the FAQ. As an
    exercise, find the relevant FAQ item.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jan 10, 2007
    #2
    1. Advertising

  3. Kenneth Porter wrote:

    > I often have the need to temporarily change some setting before
    > performing an operation. If the operation fails, I still need to restore
    > the original setting. It's possible for the restore operation to fail
    > (eg. a communication fault that temporarily prevents the request from
    > reaching the hardware), in which case user intervention is required. The
    > application can later re-initialize the system to a known state.

    (snip)
    > How can I capture the error if the restore operation fails? Do I simply
    > use uncaught_exception() in the RAII dtor to throw only if I'm not
    > handling an exception from the main operation?


    IMO will be better that the system works as finite state automata, and set
    the system state to "Intervention required". By means of a global function,
    of by having in the RAII object a reference to the object that maintains
    the state, or any other way appropriate for your application. The general
    rule is to avoid to throw from destructors.

    --
    Salu2
     
    =?ISO-8859-15?Q?Juli=E1n?= Albo, Jan 10, 2007
    #3
  4. On Wed, 10 Jan 2007 16:24:06 -0600, Kenneth Porter wrote:
    >With C++, it seems like I should report errors via exceptions, and


    Not necessarily. In your case an error value may be more appropriate.

    >perform the temporary settings changes with RAII objects (to insure that
    >original settings are restored through all error paths).


    Good idea.

    >How can I capture the error if the restore operation fails?


    You cannot. An exception shall never be thrown from a destructor and a
    return value is impossible. Therefore the general rule is to use only
    code in the destructor that either cannot throw or that only throws an
    exception that can safely be ignored. In the latter case the (ignored)
    exception must be cought in the destructor (preferably with catch
    (...)). Other code must be put in member functions but not in the
    destuctor.

    >Do I simply
    >use uncaught_exception() in the RAII dtor to throw only if I'm not
    >handling an exception from the main operation?


    See e.g. http://www.gotw.ca/gotw/047.htm

    Best wishes,
    Roland Pibinger
     
    Roland Pibinger, Jan 11, 2007
    #4
  5. =?ISO-8859-15?Q?Juli=E1n?= Albo <> wrote in
    news::

    > IMO will be better that the system works as finite state automata, and
    > set the system state to "Intervention required". By means of a global
    > function, of by having in the RAII object a reference to the object
    > that maintains the state, or any other way appropriate for your
    > application. The general rule is to avoid to throw from destructors.


    Ah, so instead of throwing the exception, the destructor could invoke an
    append method on a global fault object that's monitored as part of the
    background loop, and before any critical operation takes place (ie. in my
    application, anything that operates a physical actuator).
     
    Kenneth Porter, Jan 16, 2007
    #5
  6. Kenneth Porter

    Grizlyk Guest

    Kenneth wrote:

    > How can I capture the error if the restore operation fails? Do I simply
    > use uncaught_exception() in the RAII dtor to throw only if I'm not
    > handling an exception from the main operation?


    I make global stack (vector) to hold errors. When your object of class
    of error is created, the object makes "record" in the stack, so you can
    skip double throw.

    if (your_error_condition)
    {
    //here disable optimisation to prevent moving "tmp" to
    if(!uncaught_exception()) block
    //i am not shure, that it is needed
    auto volatile

    //here error added to you stack;
    Class_of error tmp;

    //here you can do not rhrow, remain old error - new error will be on
    your stack also
    if(!uncaught_exception())throw tmp;
    }

    It is not "checked by time" solution, new for me.
     
    Grizlyk, Jan 16, 2007
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Kerri
    Replies:
    2
    Views:
    13,120
    Kevin Spencer
    Oct 27, 2003
  2. Replies:
    15
    Views:
    7,767
    Roedy Green
    Sep 8, 2005
  3. Johannes Schaub (litb)

    Re: Why is RAII called RAII?

    Johannes Schaub (litb), Sep 12, 2010, in forum: C++
    Replies:
    2
    Views:
    430
    James Kanze
    Sep 18, 2010
  4. cpp4ever

    Re: Why is RAII called RAII?

    cpp4ever, Sep 12, 2010, in forum: C++
    Replies:
    1
    Views:
    431
    BGB / cr88192
    Sep 13, 2010
  5. Goran Pusic

    Re: Why is RAII called RAII?

    Goran Pusic, Sep 13, 2010, in forum: C++
    Replies:
    11
    Views:
    593
    ptyxs
    Sep 16, 2010
Loading...

Share This Page