From: Andrew on
I am working on enhancements and changes to a C++ system that uses
exceptions. A very common way the code catches exceptions is by the
base class, std::exception. I used to think there was nothing wrong
with this but my experience with this codebase is making me have
second thoughts. When I describe the issues, maybe you guys can give
your opinions.

The code uses some byzantine libraries that can throw for one of three
reasons:

1) It really needs to throw, coz there was a runtime error and they do
happen from time to time.
2) it throws std::logic_error because it has detected a situation that
could only arise as a result of a coding error.
3) Access violation. Microsoft structured exception handling (SEH)
turns these into C++ exceptions that have std::exception as the base
class. The access violations are distressingly common. They are so
common that SEH has been explicitly enabled to allow these errors. I
think the reasoning might be that access violation corresponds to
std::logic_error. Just guessing.

All 3 are dealt with by saying catch(std::exception&). This tends to
cause errors of type 2 and 3 to be swallowed. I think it should catch
std::runtime_error and let anything else bubble up to the very top
where the app will log it then terminate. Is this a good technique/
practise in general? When people create exception classes do they tend
to make them inherit from std::runtime_error? I have a horrible
feeling that many devs might inherit directly from std::exception.

Regards,

Andrew Marlow

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Goran on
> I am working on enhancements and changes to a C++ system that uses
> exceptions. A very common way the code catches exceptions is by the
> base class, std::exception. I used to think there was nothing wrong
> with this but my experience with this codebase is making me have
> second thoughts. When I describe the issues, maybe you guys can give
> your opinions.
>
> The code uses some byzantine libraries that can throw for one of three
> reasons:
>
> 1) It really needs to throw, coz there was a runtime error and they do
> happen from time to time.
> 2) it throws std::logic_error because it has detected a situation that
> could only arise as a result of a coding error.
> 3) Access violation. Microsoft structured exception handling (SEH)
> turns these into C++ exceptions that have std::exception as the base
> class. The access violations are distressingly common. They are so
> common that SEH has been explicitly enabled to allow these errors. I
> think the reasoning might be that access violation corresponds to
> std::logic_error. Just guessing.

There is a way to turn SE into C++ exceptions (_set_se_translator),
which is probably what happens in these libraries.

There is IMO a massive error with this idea: SEs often happen when
code is deep into undefined behavior land, and that means it already
crashed, it's just that it's not quite dead. Thus, trying to continue
after such an error often leads to massive errors later. Sometimes,
crashes are equivalent to what logic_error should be used in
"standard" C++ (e.g. dereferencing NULL can easily be turned into a
logic_error). But that's a small consolation, 'cause error sources are
much more numerous and insidious than that. But I digress...

What your libs do seem quite common. This:

"The access violations are distressingly common."

however, should not be. An AV is a crash, and should be fixed.
(Strictly speaking, an AV is actually caused by invoking undefined
behavior first). That's the end of it for any self-respecting code,
really.

> All 3 are dealt with by saying catch(std::exception&).

catch(const exception_type&) is preferred (note the "const").

> This tends to
> cause errors of type 2 and 3 to be swallowed. I think it should catch
> std::runtime_error and let anything else bubble up to the very top
> where the app will log it then terminate. Is this a good technique/
> practise in general?

Quite good IMHO. For case 3, even better thing IMO is to crash,
produce a crash dump, and use debugging information to look for the
actual cause. This is because a crash dump will be richer in info than
any exception one might throw from SE translator function.

> When people create exception classes do they tend
> to make them inherit from std::runtime_error? I have a horrible
> feeling that many devs might inherit directly from std::exception.

Why is that so horrible? The problem is not really common base class,
the problem is the absence of more selective catch statements. Things
really are as you say: runtime_error is recoverable, but logic_error,
much less, if at all. SE is IMO, NOT recoverable at all.

Ideally, code should just catch(const runtime_error&) (or it's
specialization) for all try/catches except top-level ones (top-level
try/catch: the one that wraps main() and any thread functions); top
level should catch logic_error. That's it, really. See? Exceptions are
simple! :-)

I don't agree with your idea that you should have exceptions that do
not derive from std:exception so that you can e.g. prevent people from
catching programming errors (logic_error) in bad places. Sure, you can
use something else, but you are one catch(something_else&) away from
being in the same situation - again. And since catch-es are rare (see
P.P.S), it's not hard to find eventual catch(logic_error)-es, if they
re-appear. :-)

Goran.

P.S. if you often find yourself writing this:

try { work } catch(const exception&)
{
non-throwing_cleanup();
throw
}

use ScopeGuard of Alexandrescu. Best thing since sliced bread. Heck,
this thing alone is worth more than half of boost ;-).

P.P.S. my rule 0 of exceptions in C++ is: ___you are not allowed to
write try-catch statements___. If you think you need one, first make a
failing test case and see where it ends up being caught!

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Mathias Gaunard on
On Aug 5, 8:54 pm, Andrew <marlow.and...(a)googlemail.com> wrote:

> 2) it throws std::logic_error because it has detected a situation that
> could only arise as a result of a coding error.

I would suggest using an assert instead.


> 3) Access violation. Microsoft structured exception handling (SEH)
> turns these into C++ exceptions

I believe this is disabled by default in recent versions of MSVC, and
should be kept disabled.



--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Nick Hounsome on
On 5 Aug, 20:54, Andrew <marlow.and...(a)googlemail.com> wrote:

> All 3 are dealt with by saying catch(std::exception&). This tends to
> cause errors of type 2 and 3 to be swallowed. I think it should catch
> std::runtime_error and let anything else bubble up to the very top
> where the app will log it then terminate. Is this a good technique/
> practise in general?

Better than catch(exception) but you shouldn't really swallow any
exception unless you know what it means and you know what to do about
it and since you don't know what runtime_error really means (you only
know what it doesn't mean) it's hard to see why you would catch it.
For example suppose that you are just a lowly function called as part
of a higher level transaction. If you swallow the exception the
transaction will be committed as successful even though it wasn't.

Contrariwise it might just make sense to catch std::exception provided
that you are pretty sure that the function called has no side effects
(i.e. it is a pure function ) and you can either notify the user or
work around it.

> When people create exception classes do they tend
> to make them inherit from std::runtime_error? I have a horrible
> feeling that many devs might inherit directly from std::exception.

Many have their own hierarchy based on std::exception.

Even Bjarne Stroustrop isn't a fan of the hierarchy defined in the
standard.

IMHO it would probably be better if there had been something like
java's Throwable at the bottom of the hierarchy where you could put
stuff like bad_alloc which you really don't want to catch under any
circumstances.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Kenneth 'Bessarion' Boyd on
On Aug 7, 5:18 am, Nick Hounsome <nick.houns...(a)gmail.com> wrote:

> IMHO it would probably be better if there had been something like
> java's Throwable at the bottom of the hierarchy where you could put
> stuff like bad_alloc which you really don't want to catch under any
> circumstances.

Well, any circumstances other than maintaining 100% uptime.

std::bad_alloc is the only standard exception that is obviously
*reliably* recoverable from. At least, when the target platform's
memory manager is honest and the problem domain is loosely coupled
enough (this describes my usual problem domains). So it's the one I
normally think about catching. [Of course, always verify that NULL
return isn't a better option than throwing std::bad_alloc during the
first implementation pass.]

The entire std::logic_error and std::runtime_error hierarchies look
like an assert replacement for applications that want 100% uptime.
(Which doesn't describe my usual problem domains, but to the
inexperienced would describe a server-type application very well.)


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

 |  Next  |  Last
Pages: 1 2
Prev: Loki::Function
Next: Right Endian conversion