I was writing code with exception handling the other day, and I had a few questions about exceptions, their guarantees and throwables.
Basically, say you have:
class X {
string m_str;
X() : m_str("foo")//what if this throws?
{
ifstream b("a.in")//what if this throws?
}
And after running through all the articles I could find, I still have no idea what is the clean way to handle this.
Say I have a code like:
{
...
X myInstanceOfClassX;
...
}
Should I wrap the code in catch(exception &)
? And if I do that, does string
and ifstream
guarantee a strong guarantee, that no resources are leaked and nothing has been left half opened?
Also, if my class throws myexception
, that is derived from exception, catch(exception &)
seems to let it through. So that leaves me with catch(...)
which IIRC catches access violation?.? Is there another way?
Then there was a piece of information somewhere that any exception thrown in subconstructors of object constructor shouldn't be caught, and constructor should throw in case any of the member objects throw.
And what if the code above would have been called not from constructor, but from regular a function void foo()
, which exceptions should I catch? outofmemory_something, filenotfound_something? Where can I find the definitions of what STL objects can throw? Are they implementation specific?
Where is the authoritative source where I could clear all my doubts and questions on this topic?
So far, it seems that handling exceptions is like dancing in a big pile of gooo. Error codes seem A LOT simpler and safer...
If either of these throw
class X {
string m_str;
X() : m_str("foo")//what if this throws?
{
ifstream b("a.in")//what if this throws?
}
Then the object you were creating will not exist.
If an exception is thrown in the constructor of an object, then all fully created members are destructed (using the their destructor) and memory for the object is returned to the system. Thus any members that are not fully constructed at the throw point will not be destroyed (as they have not been created).
Should I wrap the code in catch(exception &)? And if I do that, does string and ifstream guarantee a strong guarantee, that no resources are leaked and nothing has been left half opened?
Even if you catch an exception (outside the object) there is no object to work on as it never existed (the object only starts its lifespan after the constructor completes).
In the above you are guaranteed there are no leaks or open resources.
Also, if my class throws myexception, that is derived from exception, catch(exception &) seems to let it through. So that leaves me with catch(...) which IIRC catches access violation?.? Is there another way?
If your exception is derived from std::exception then catch(std::exception&)
will work. If it is not working then you are doing something wrong (but we need more detail (like the code that throws and the code that catches, an English description is not adequate)).
Then there was a piece of information somewhere that any exception thrown in subconstructors of object constructor shouldn't be caught, and constructor should throw in case any of the member objects throw.
Probably the best option and as a general rule not bad advice.
And what if the code above would have been called not from constructor, but from regular a function void foo(), which exceptions should I catch? outofmemory_something, filenotfound_something? Where can I find the definitions of what STL objects can throw? Are they implementation specific?
You should only catch exceptions if you can do something about it. Usually this is nothing so don;t catch them let the application quit normally (via the exception unwinding the stack).
Where is the authoritative source where I could clear all my doubts and questions on this topic?
You're question are so varied that that is hard.
I could recommend "Exceptional C++" by Herb Sutter.
So far, it seems that handling exceptions is like dancing in a big pile of gooo. Error codes seem A LOT simpler and safer...
You are wrong there. Exceptions are much easier. You just seem to be over-thinking it and getting confused. That is not to say that error codes do not have their place.
If something goes wrong and you can not fix it locally
then throw an exception. All the classes in the standard are designed with exception in mind and will behave correctly. So that just leaves your classes.
That's it, the rest auto-magically works.
Every function has a precondition and a postcondition. The correct time to throw an exception is when the postconditions cannot be satisfied. There is no other correct time.
There are two special cases.
The postcondition for a constructor is the existence of a valid object, therefore throwing is the only reasonable way to report an error. If you have something like a Foo::is_ok()
test then what you have is a valid object which represents an invalid state.
The postcondition for a destructor is the nonexistence of an object, therefore throwing is never a reasonable way to report an error. If you have something tricky to do at the end of an object's life, do it as a separate Foo::commit()
member function call.
Beyond this you have options, and it's a matter of taste.
For example
std::vector::operator[]
does not check preconditions and is noexcept(true)
, butstd::vector::at()
does check, and throws.The choice is whether or not you assume your preconditions are valid. In the first case you are using design-by-contract. In the second case, given that you have detected that they are not, you know the postconditions cannot be valid and therefore should throw; in the first case you assume they are and, given that, the postconditions must be valid and therefore you never need to.
GOTW covers a lot of the dark corners of exceptions and demonstrates nicely why things are what they are.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With