Inline try/catch

Started by
18 comments, last by l0calh05t 10 years, 8 months ago

But you never ever want to catch anything by value (slicing) - assuming C++ of course. You always want to throw by value and catch by reference, or else bad things may happen.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley
Advertisement

You already have stack space for that. of course assuming the exception do not allocate extra memory in copy ctor...

Peace and love, now I understand really what it means! Guardian Angels exist! Thanks!

You already have stack space for that

Stack space for what?

[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

Note that throwing an exception from a destructor is also super bad mojo in C++. Basically any exception guarantees go out the window if you have destructors that throw. It's sufficiently bad that C++11 gave all destructors an implicit noexcept exception specification, meaning that an exception from a destructor will immediately call terminate() unless you specifically enable exceptions for the destructor for the class or one of the subobjects of that class contains a destructor which has exception enabled destructors.

You already have stack space for that

Stack space for what?

For passing by value.

Peace and love, now I understand really what it means! Guardian Angels exist! Thanks!

You still don't want to catch by value though, since the object will be sliced if it is a derived class and you catch the base class.

See: http://stackoverflow.com/questions/2522299/c-catch-blocks-catch-exception-by-value-or-reference

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

Note that throwing an exception from a destructor is also super bad mojo in C++. Basically any exception guarantees go out the window if you have destructors that throw. It's sufficiently bad that C++11 gave all destructors an implicit noexcept exception specification, meaning that an exception from a destructor will immediately call terminate() unless you specifically enable exceptions for the destructor for the class or one of the subobjects of that class contains a destructor which has exception enabled destructors.

Actually I find this one of the more valuable additions to C++11, because if your destructor throws, something is seriously fucked up. At least, usually... there may be singular cases where it may be allowable and desirable, but I must admit I don't see them. I do see the problems, though.

Either you have a "conditional destructor" with some (hopefully bulletproof) logic that prevents it from throwing while being unwound, then your class behaves differently although the expectation would be that it works the same. You now need a separate way of detecting the error condition, or you must simply ignore the error. Either way, this stinks.

Or, you are unaware of what happens when you throw from a destructor during unwind, and do it anyway. This is even worse, in two respects (first, because you don't know the implications, and second because it actually happens, and the program is now in an undefined, unrecoverable state). And, what's worst, now you don't even know what happened, or where.

So, for that this case, crashing hard by terminating the program is a good thing. It forces the programmer to address the issue. Actually, the mere presence of the throw keyword inside a destructor should preferrably generate a build error, if you ask me.

One problem I see with inlineing try catch like that is that it makes it too easy to use. Throwing and catching an exception is an expensive operation, and it's good when the code reflects that complexity. You don't want to encourage non exceptional cases to throw exceptions for the sake of convenient syntax.

Ruby allows this with a simpler syntax.

'Normal' exception handling:


begin
  10 / 0
rescue
  puts "Please do not divide by zero."
ensure #the 'ensure' section will always execute when the block exits, regardless of exceptions
  puts "Moving right along..."
end

'Inline' exception handling:


10 / 0 rescue puts "Please do not divide by zero."
puts "Moving right along..."

In the 'inline' case, the handler extends to the end of the line. You can have multiple statements if you use semicolons to separate them.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

Note that throwing an exception from a destructor is also super bad mojo in C++. Basically any exception guarantees go out the window if you have destructors that throw. It's sufficiently bad that C++11 gave all destructors an implicit noexcept exception specification, meaning that an exception from a destructor will immediately call terminate() unless you specifically enable exceptions for the destructor for the class or one of the subobjects of that class contains a destructor which has exception enabled destructors.


Since all guarantees go out of the window if you throw in the destructor it is a *good thing* that destructors are marked noexcept, that way you have a guarantee that terminate is called, which I'll take over all kinds of undefined behavior (which throwing in the destructor will cause when used in conjunction with pretty much any library class or template which doesn't expect that... which includes all standard containers) any day.

This topic is closed to new replies.

Advertisement