C++ Logo

std-discussion

Advanced search

Re: Are Exceptions deeply flawed?

From: Brian Bi <bbi5291_at_[hidden]>
Date: Wed, 7 Aug 2019 16:06:10 -0500
On Wed, Aug 7, 2019 at 3:10 PM Dmitry via Std-Discussion <
std-discussion_at_[hidden]> wrote:

> I really don't want to create temporary files anymore, since that implies
>> removing them at some point. So I really need memfds. So I can't do your
>> first
>> point.
>>
> What will you do if this feature is not available (your program is running
> on older kernel)?
>
>
>> Sorry, you don't get to change the API. Using the API as-is was the big
>> problem of your post. If you can change my API to make it better and
>> report a
>> more structured set of information, then the same can be done too for
>> exceptions.
>
> That is really interesting and that is what I cannot fully understand, can
> you please elaborate how exactly it can be done via exceptions? Of course,
> in a maintainable way (a way that can easily accommodate adding/removing
> sub-errors *as well as adding new direct/indirect callers* of the
> function in the future), without relying on documentation nor on comments
> etc...?
>
>
>>
>> Your solution requires that at some point in the call stack, the error
>> condition be consumed and then either augmented or handled. Nothing in
>> that
>> requires using return values, global/thread-local values (like errno) or
>> exceptions. All three are possible.
>>
> All three are possible, not all of them are as visible and explicit as
> return-values.
>
>
>> What you're arguing for is sanely designed API and handling error
>> conditions
>> as close to the error locus as possible. I don't dispute that: that's the
>> proper and only way to handle errors. What I will agree with you is that
>> using
>> exceptions makes it to easy *not* to do it, since you can simply "forget"
>> to
>> handle, then let the error pass on to your caller. When using return
>> values,
>> the error condition will often be "in your face" and you'll have a hard
>> time
>> ignoring it.
>>
>> But C++ does not dictate what you must do. You don't like exceptions? Or
>> don't
>> feel comfortable writing code with it, like me? Then don't use them. But
>> don't
>> ask that they be removed from the language. That's not going to happen.
>>
>
> Yes, I, probably, agree with you.
> But, should not then exceptions be discouraged from using exactly as
>
> - bare new/malloc (in favour of make_unique/make_shared)
> - or void * (in favour of type-rich programming)
> - or default implicit constructors
>
> are discouraged now?
>

I could get on board with the idea that we should discourage people from
using exception handling in situations where return codes would be a
*better* solution but you have not established that return codes are *almost
always better* than exceptions for *almost all programmers*. This is quite
different from the situation with smart pointers versus manual memory
management.

I think exceptions are only compelling in situations where some condition
occurs that the caller may not be able to handle. As an example, an
iterator that accesses data from a file may, during an execution of
operator++, encounter an I/O error. At a high level, this may have been
caused by a network disconnection, interruption by a signal, or various
more obscure causes. Now the iterator may have been passed as an argument
to a parser library - the library consumes bytes from the iterator and
generates tokens as output. The parser wouldn't know what to do about the
I/O error either. If an exception had been thrown by the iterator, it may
bubble up to the application that is using the parser library, which might
(having already handled the signal) simply continue the parsing, or check
for a network disconnection and ask the user to check whether a cable is
unplugged, and do whatever other recovery steps are necessary.

I think this is an example where using return codes doesn't add any value,
because the only thing the parser is going to do is forward the return code
- it does not need to add any context in order for the caller to handle the
exception. Note also that the question of the probability of the
"exceptional" events occurring is not relevant here, except perhaps in a
performance-sensitive context. Whether you want to make that iterator throw
an exception on an I/O error, or return expected<char, E>, regardless of
the fraction of the time that the error path is taken, is a stylistic
choice. Either way, the caller will either know what to do about the
condition, or bubble up the error to its caller. In the case with the
exceptions, the intermediate caller that doesn't know how to handle the
error doesn't need to have any extra code, but can be written
"optimistically".

In addition - as someone who has worked on a code base where exceptions are
banned - I have to say that I don't buy the "exceptions lead to crashes in
production" logic. If an exceptional condition occurs, then either you know
how to handle it, or you don't. In the case where you don't, what happens
when you get the error code you don't know how to handle? At worst, the
program ignores it and continues to execute, possibly taking actions that
make no sense, writing bogus data to your database, etc. At best, you
decide to crash the program and someone gets paged (hopefully you have some
load balancer so the other servers can handle requests while you debug the
server that crashed).

The idea that at least with return codes, you can know at compile time that
there's something you haven't accounted for, is interesting but I believe
it has its issues as well. There will be cases where sloppy programmers
will just write a `default:` that propagates the error code (which is
equivalent to not handling an exception), or which logs the error and then
swallows it (which is equivalent to `catch (...) {}`, but by definition,
you cannot know whether this is a sensible thing to do or whether it is
necessary to crash the program). In fact, I assume that in many cases you
are going to do this despite the fact that I have full confidence that you
are a good programmer---because the caller may have enough context to know
that, given the manner in which it is using the callee, a certain error
code cannot occur at runtime. But then, like you said, you may update the
library that contains the callee, and suddenly that error can occur, and
now your code is broken, with no hint from the compiler.


> Even if the answer is no, should not we add expected<> (for symmetry or as
> mechanism that complEments exceptions) into the standard?
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>


-- 
*Brian Bi*

Received on 2019-08-07 16:08:23