exception: use of error_info can mask real exceptions
Just wanted to ask if there were commonly accepted solutions to the
following problem:
typedef boost::error_info
Just wanted to ask if there were commonly accepted solutions to the following problem:
typedef boost::error_info
f1_descr; and then
throw f1exception() << f1_descr("blah blah blah").
and later
catch(f1exception & ex){...}
The constructor of std::string may throw an exception in which case f1exception will never be thrown. This is not necessarily the desired outcome as the intention was obviously to indicate f1exception error, not the string() error.
This problem pertains to any type that throws exceptions in its constructor and is used with error_info idiom.
What may be more appropriate is something like this
typedef boost::error_info
f1_descr ... error condition is detected shared_ptr<string> pErrStr; try { pErrStr = new string("Error description"); } catch(bad_alloc & ex) { } throw f1exception() << f1_descr(pErrStr);
Meaning that if string fails to allocate memory, I'm willing to forgo the error description but still throw the intended exception.
Is there a cleaner way to do that?
Thanks, Andy.
I spoke with Emil about this at BoostCon. He understood and said that he would be looking at making this the default behavior. Jon
On Wed, May 20, 2009 at 8:17 PM, Jon Kalb
Just wanted to ask if there were commonly accepted solutions to the following problem:
typedef boost::error_info
f1_descr; and then
throw f1exception() << f1_descr("blah blah blah").
and later
catch(f1exception & ex){...}
The constructor of std::string may throw an exception in which case f1exception will never be thrown. This is not necessarily the desired outcome as the intention was obviously to indicate f1exception error, not the string() error.
Jon Kalb raised this issue at BoostCon. My knee-jerk reaction was that it makes sense to alter the specification of operator<< so it never throws, but now my opinion is that it isn't a good idea. Consider this slightly modified example: std::string get_descr(); .... throw f1exception() << f1_descr(get_descr()); Even if operator<< and f1_descr's constructor never throw, in theory you could still get std::bad_alloc instead of f1exception. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
<...>
Jon Kalb raised this issue at BoostCon. My knee-jerk reaction was that it makes sense to alter the specification of operator<< so it never throws, but now my opinion is that it isn't a good idea.
Well, in my original post I didn't mean just the inner workings of operator<<. By the time operator<< is invoked, the temporary object of std::string (or other type for that matter) will have to be constructed. That means, that the exception may be thrown before getting into operator<< Unfortunately, it seems that using '<<' syntax it will be impossible to catch exceptions. I hate macros as much as the next guy, but it looks like this sort of problem is solvable only using macros.
Consider this slightly modified example:
std::string get_descr();
.... throw f1exception() << f1_descr(get_descr());
Even if operator<< and f1_descr's constructor never throw, in theory you could still get std::bad_alloc instead of f1exception.
Exactly! That's what I meant in my original post.
Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
Regards, Andy.
On Sun, May 24, 2009 at 11:46 AM, Andrew Venikov
Consider this slightly modified example:
std::string get_descr();
.... throw f1exception() << f1_descr(get_descr());
Even if operator<< and f1_descr's constructor never throw, in theory you could still get std::bad_alloc instead of f1exception.
Exactly! That's what I meant in my original post.
What you want is, once you've reached a point in the program that is going to throw an exception, to guarantee that you will throw that exception and not something else. The problem is that this has to extend to all failures that can occur within the throw expression. Such failures are not limited to bad_alloc; any function you call in the throw expression may throw any exception. I'm reasonably certain that you don't want those to be ignored, do you? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
<...>
What you want is, once you've reached a point in the program that is going to throw an exception, to guarantee that you will throw that exception and not something else.
Precisely.
The problem is that this has to extend to all failures that can occur within the throw expression. Such failures are not limited to bad_alloc; any function you call in the throw expression may throw any exception. I'm reasonably certain that you don't want those to be ignored, do you?
No, I wouldn't want those to be ignored. Ideally, I would want to know about all failures. But if I had to choose, I would choose to have knowledge about the original exception. Here's my reasoning: The original exception, one would think, indicated an error condition in the system proper. The exception that occurred during construction of an object describing a failure, really has nothing to do with the system proper. It may indirectly indicate that there's something wrong with the system, but I'm way more interested in why the original exception happened. What if system entered an unrecoverable state due to the original error condition? And the failure to initialize an error description just follows from the original error? In that case getting an exception about error descriptor's failure is not nearly as informative as getting the original cause of the error. Take the simplest, and in my opinion the most probable scenario. 1.The system runs out of memory trying to allocate a certain resource. 2.An exception that indicates the condition and resource is thrown. 3.We try to add a description to the exception thrown, description requires memory. 4. But, since we're already out of memory, we fail creating the error description. What exception would you rather want to receive in this case? For example: AwesomeWidget * pWidget = MakeAwesomeWidget(); if (!pWidget) { throw AwesomeWidgetError() << OwesomeWidgetDescriptor("more information here"); } If construction of OwesomeWidgetDescriptor fails but I still get AwesomeWidgetError(), then at least I know where and what kind of error I encountered. There's even a good chance that error condition was caused by allocating too many AwesomeWidgets, or leaking them. If, on the other hand, I get an exception thrown from OwesomeWidgetDescriptor initialization code, I will get an unrelated exception. I will not know that the error actually happened while trying to create an AwesomeWidget. Andy.
On Sun, May 24, 2009 at 5:11 PM, Andrew Venikov
<...>
What you want is, once you've reached a point in the program that is going to throw an exception, to guarantee that you will throw that exception and not something else.
Precisely.
The problem is that this has to extend to all failures that can occur within the throw expression. Such failures are not limited to bad_alloc; any function you call in the throw expression may throw any exception. I'm reasonably certain that you don't want those to be ignored, do you?
No, I wouldn't want those to be ignored.
That's my point, you want some exceptions emitted by the throw expression to be ignored, and some not. Even if that was a good idea, you have to come up with an intuitive criteria which isn't going to surprise other users.
1.The system runs out of memory trying to allocate a certain resource. 2.An exception that indicates the condition and resource is thrown. 3.We try to add a description to the exception thrown, description requires memory. 4. But, since we're already out of memory, we fail creating the error description.
What exception would you rather want to receive in this case?
In my mind, the postcondition of throw foo() << info1(...) << info2(...) is that a foo exception containing info1 and info2 is thrown. If that postcondition can not be satisfied, I do want (another) exception that tells me why. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
Emil Dotchevski wrote: <...>
That's my point, you want some exceptions emitted by the throw expression to be ignored, and some not. <..>
I would rather say that the main exception should be thrown no matter what. As I described in my previous post, the main, or original exception, indicates an error in the main system/logic. The exception thrown while trying to construct an error description is just that - an error to create additional description about an already occurred error. Of course it would be nice to know why both of them happened, but here you must chose - either you know what went wrong in the main program, or what went wrong in error_info. When I write this: throw WidgetAllocError(); It's pretty obvious what I want. Of course, it would be nice to have more informative description, that's why I would normally add: throw WidgetAllocError() << WidgetErrorDescription(strWidgetName); But this now changes the original intent. By the time we reach the throw statement, we've made a decision that an error condition has been reached and we want to indicate that error. If we continue propagating WidgetAllocError(), then we'll at least know what kind of error happened, albeit without extra information. If we let error_info throw, then we'll get most probably an unrelated error (like std::bad_alloc) which doesn't tell you anything about the context of the original main error. I think that std::exception's constructor is a no-throw exactly for this reason - once you've made your decision to indicate an error you're guaranteed that error's propagation. I think it's similar to a requirement that destructors don't throw. Once someone threw an exception, then even if something goes wrong in a destructor, we shouldn't throw new exception.
1.The system runs out of memory trying to allocate a certain resource. 2.An exception that indicates the condition and resource is thrown. 3.We try to add a description to the exception thrown, description requires memory. 4. But, since we're already out of memory, we fail creating the error description.
What exception would you rather want to receive in this case?
In my mind, the postcondition of throw foo() << info1(...) << info2(...) is that a foo exception containing info1 and info2 is thrown. If that postcondition can not be satisfied, I do want (another) exception that tells me why.
I understand. But you have to choose. Don't you want to know that foo() was thrown in the first place? Because you won't know that if you throw another exception. Regards, Andy.
On Sun, May 24, 2009 at 10:09 PM, Andrew Venikov
Emil Dotchevski wrote:
<...>
That's my point, you want some exceptions emitted by the throw expression to be ignored, and some not. <..>
I would rather say that the main exception should be thrown no matter what.
If you write: throw foo() << info1(bar()); and bar() throws something (note, this function might have nothing to do with Boost Exception or foo), do you want that exception silently ignored?
When I write this:
throw WidgetAllocError();
It's pretty obvious what I want. Of course, it would be nice to have more informative description, that's why I would normally add:
throw WidgetAllocError() << WidgetErrorDescription(strWidgetName);
But this now changes the original intent. By the time we reach the throw statement, we've made a decision that an error condition has been reached and we want to indicate that error. If we continue propagating WidgetAllocError(), then we'll at least know what kind of error happened, albeit without extra information.
That "albeit" is very important. It effectively requires the catch site to be able to deal with a WidgetAllocError that has no additional information in it. Are you sure you want to impose this requirement on all programs wrt all exceptions? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
Emil Dotchevski wrote:
On Sun, May 24, 2009 at 10:09 PM, Andrew Venikov
wrote: Emil Dotchevski wrote:
<...>
That's my point, you want some exceptions emitted by the throw expression to be ignored, and some not. <..> I would rather say that the main exception should be thrown no matter what.
If you write:
throw foo() << info1(bar());
and bar() throws something (note, this function might have nothing to do with Boost Exception or foo), do you want that exception silently ignored? By the same token I could ask: do you want foo() silently ignored?
When I write this:
throw WidgetAllocError();
It's pretty obvious what I want. Of course, it would be nice to have more informative description, that's why I would normally add:
throw WidgetAllocError() << WidgetErrorDescription(strWidgetName);
But this now changes the original intent. By the time we reach the throw statement, we've made a decision that an error condition has been reached and we want to indicate that error. If we continue propagating WidgetAllocError(), then we'll at least know what kind of error happened, albeit without extra information.
That "albeit" is very important. It effectively requires the catch site to be able to deal with a WidgetAllocError that has no additional information in it. Are you sure you want to impose this requirement on all programs wrt all exceptions?
Agreed. But if I understand correctly, even now the good style to catch error_info is first to call get_error_info(), and then check the pointer for null. "Imposing this requirement" here simply means doing that check for pointer not being equal to null. Or am I missing something? Are you saying there is a way where we can assume that the returned pointer will never be null? It looks like we really have to decide, what's more important - preserving the original exception or preserving a newer exception. I'm trying to understand whether this dilemma simply presents a matter of taste, or is there a real (if a little academical) problem to solve. If I'm convinced that it's just a matter of taste - then I'm fine with either way. But so far I feel that importance of receiving the original exception outweighs the possible loss of other exceptions (which by the way wouldn't be thrown in a first place if it wasn't for the original exception) Maybe others can chime in. Thanks, Andy.
Andrew Venikov wrote:
It looks like we really have to decide, what's more important - preserving the original exception or preserving a newer exception. I'm trying to understand whether this dilemma simply presents a matter of taste, or is there a real (if a little academical) problem to solve. If I'm convinced that it's just a matter of taste - then I'm fine with either way. But so far I feel that importance of receiving the original exception outweighs the possible loss of other exceptions (which by the way wouldn't be thrown in a first place if it wasn't for the original exception)
Maybe others can chime in.
I think it almost never matters, and it especially doesn't matter when the second exception is std::bad_alloc. When you run out of memory, you have to abort the current operation. It doesn't matter if you ran out of memory before or after another failure occurred. The difference between exceptions only matters if you can somehow recover from one exception but not the other. In this case, the more severe exception - the one from which it is harder to recover - should have priority. std::bad_alloc is almost always the severest exception possible because it is almost impossible to reliably recover from it. Therefore std::bad_alloc should usually have priority. -- Rainer Deyke - rainerd@eldwood.com
On Sun, May 24, 2009 at 11:02 PM, Andrew Venikov
When I write this:
throw WidgetAllocError();
It's pretty obvious what I want. Of course, it would be nice to have more informative description, that's why I would normally add:
throw WidgetAllocError() << WidgetErrorDescription(strWidgetName);
But this now changes the original intent. By the time we reach the throw statement, we've made a decision that an error condition has been reached and we want to indicate that error. If we continue propagating WidgetAllocError(), then we'll at least know what kind of error happened, albeit without extra information.
That "albeit" is very important. It effectively requires the catch site to be able to deal with a WidgetAllocError that has no additional information in it. Are you sure you want to impose this requirement on all programs wrt all exceptions?
Agreed. But if I understand correctly, even now the good style to catch error_info is first to call get_error_info(), and then check the pointer for null.
This has nothing to do with style. The following program is valid:
#include
It looks like we really have to decide, what's more important - preserving the original exception or preserving a newer exception.
No, the decision is between ignoring the failure to throw an exception with specific error info in it or not ignoring it. By the way, if you want to ensure that throw foo() will necessarily throw foo, that's not possible. Throwing an exception requires memory, and if that memory is not available, throwing std::bad_alloc instead is allowed behavior (the implementation is required to always be able to throw std::bad_alloc.) Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
Andrew Venikov:
I would rather say that the main exception should be thrown no matter what. As I described in my previous post, the main, or original exception, indicates an error in the main system/logic. The exception thrown while trying to construct an error description is just that - an error to create additional description about an already occurred error.
An exception should never indicate a logic error, because logic errors should never occur (and when they do, they are generally not recoverable.) It is not clear to me why you insist that the second exception has nothing to do with "the main system". If the main system is out of memory, it is out of memory. It cannot just be out of memory for some secondary tasks as creating an error description. In the general case, I'd argue that the second exception should be treated as more critical than the first, because the first exception can well happen in a context where it's normal for something to fail, whereas the second occurs in a context where it's not normal for something to fail (namely, the error handling branch).
It looks like we really have to decide, what's more important - preserving the original exception or preserving a newer exception. I'm trying to understand whether this dilemma simply presents a matter of taste, or is there a real (if a little academical) problem to solve.
It's a bit like pricing the credit default swap of, say, Germany. Germany will never default, and if it does, the party issuing the CDS is unlikely to be solvent and pay, so the price is random noise. Similarly, secondary exceptions overwriting the first one (the horror) are so rare that it's not possible to "price" the alternatives in an accurate manner (that is, to test them in practice.) I'd say that the only situation in which something like that can occur is with (an equivalent of) bad_alloc "overwriting" another (equivalent of) bad_alloc. In this case it is not clear to me why would one insist of knowing the precise bad_alloc that was thrown since an out of memory condition is an out of memory condition, no matter where it occurs. You may well want to know that a certain memory pool has been exhausted, but if you do, simply do not allocate in the error handling code from that same pool. Either way, given throw foo() << expr1 << expr2; if you really want to reliably throw foo() and consider expr1 and expr2 optional, the way to do it is: foo x; try { x << expr1 << expr2; } catch( ... ) {} throw x;
No, I wouldn't want those to be ignored. Ideally, I would want to know about all failures. But if I had to choose, I would choose to have knowledge about the original exception. Here's my reasoning:
I agree entirely. I would appreciate if there was way of filling exceptions with data and throwing them which has guarantees of not masking the original exception with a different one caused by the filling itself. Adam Badura
Andrew Venikov wrote:
Unfortunately, it seems that using '<<' syntax it will be impossible to catch exceptions. I hate macros as much as the next guy, but it looks like this sort of problem is solvable only using macros.
Well, when I said that in my previous post, I spoke too soon.
It looks like there actually may be a way. At least for the simple
cases. The problem currently is that error_info constructor
expects an already constructed object of value_type. The construction
of value_type happens outside of error_info's constructor, so we
can't catch it.
But if we generate error_info in such a way that we would mimic all
value_type's constructors, e.g. generate error_info constructors with
value_type's signatures, then we could do value_type construction
right in error_info constructor body, thus having an ability to catch
all exceptions. If exception happens, then we would make the current
error_info essentially a dud. If not, then we could swap out the local
object with a value_type member of error_info.
To generate these constructors we would use SFINAE with a dummy
construction of value_type using error_info's argument types.
Like this:
template
participants (7)
-
Adam Badura
-
Andrew Venikov
-
Andrew Venikov
-
Emil Dotchevski
-
Jon Kalb
-
Peter Dimov
-
Rainer Deyke