![]() |
RAII and cleanup functions that can throw
I've read this article and have some followup questions.
http://groups.google.com/group/comp....wse_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? |
Re: RAII and cleanup functions that can throw
* Kenneth Porter:
> I've read this article and have some followup questions. > > http://groups.google.com/group/comp....wse_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? |
Re: RAII and cleanup functions that can throw
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 |
Re: RAII and cleanup functions that can throw
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 |
Re: RAII and cleanup functions that can throw
=?ISO-8859-15?Q?Juli=E1n?= Albo <JULIANALBO@terra.es> wrote in
news:45a57b92_3@x-privat.org: > 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). |
Re: RAII and cleanup functions that can throw
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. |
| All times are GMT. The time now is 05:53 AM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.