Boost logo

Boost :

Subject: Re: [boost] Noexcept
From: Emil Dotchevski (emildotchevski_at_[hidden])
Date: 2017-06-12 21:26:01


On Mon, Jun 12, 2017 at 1:50 PM, Andrzej Krzemienski via Boost <
boost_at_[hidden]> wrote:

> 2017-06-12 22:28 GMT+02:00 Emil Dotchevski via Boost <
> boost_at_[hidden]>
> :
>
> > On Mon, Jun 12, 2017 at 1:15 PM, Andrzej Krzemienski via Boost <
> > boost_at_[hidden]> wrote:
> >
> > > 2017-06-12 20:07 GMT+02:00 Emil Dotchevski via Boost <
> > > boost_at_[hidden]>
> > > :
> > >
> > > > On Mon, Jun 12, 2017 at 2:42 AM, Niall Douglas via Boost <
> > > > boost_at_[hidden]> wrote:
> > > >
> > > > > On 12/06/2017 09:22, Emil Dotchevski via Boost wrote:
> > > > > > The lively debates during the Outcome review show that there is a
> > > great
> > > > > > deal of interest in solving the problem of error handling in
> > > > environments
> > > > > > where C++ exception handling is unavailable.
> > > > > >
> > > > > > Noexcept is a new C++11 library that implements a different
> > approach
> > > to
> > > > > > solving the same problem. Any feedback is welcome.
> > > > > >
> > > > > > https://zajo.github.io/boost-noexcept/
> > > > >
> > > > > The use of functional throw(), try() and catch() was a design
> > approach
> > > > > rejected very early by me and most who have looked into this
> problem.
> > > > >
> > > > > Nobody wants to reimplement via a library exception handling with
> > > > > exceptions disabled. It's an impoverished experience, and leads to
> > > > > brittle code.
> > > >
> > > >
> > > > Can you elaborate? My understanding is that the problem with
> exception
> > > > handling is the unpredictability of the performance you'll get.
> > Noexcept
> > > > directly addresses that issue by not introducing the unpredictability
> > of
> > > > its own return type which may or may not get optimized.
> > > >
> > > > It also removes the redundancy of requiring types which already have
> a
> > > > useful empty state to be wrapped into something like outcome<>.
> Nobody
> > > > would return optional<FILE *> from a function that may fail, they'll
> > just
> > > > return FILE */nullptr. Returning outcome<FILE *> is similarly
> redundant
> > > and
> > > > possibly inefficient.
> > > >
> > > >
> > > > > Just enable C++ exceptions if you want exceptions.
> > > > >
> > > >
> > > > I agree, the question is what to do if you can't.
> > > >
> > >
> > > There is a number of expectations people have or might have form
> > > error-handling framework:
> > >
> > > 1. Predictable times.
> > > 2. When I forget to check for error, the computation should not
> silently
> > > proceed.
> > > 3. Not polluting the function return type
> > > 4. Explicit control flows.
> > > 5. No explicit control flows.
> > > 6. Neutrality for some functions, elspecially those extern "C".
> > > 7. Being fast.
> > > 8. Being able to carry any payload.
> > >
> > > Obviously, a framework cannot guarantee all of these, and trade-offs
> need
> > > to be made. One thing that both exceptions, and outcome<>/expected<>
> have
> > > is #2: when you forget that the function might fail, and it fails, the
> > > dependent functions will not get called.
> >
> >
> > What do you mean "dependent functions"?
> >
> >
> > > In case of exceptions this is
> > > owing to stack unwinding. In case of outcome, it is because the program
> > > will not compile.
> >
> >
> > Can you post an actual example so we're not talking in the abstract?
> >
>
> ```
> int job(int x)
> {
> int y = f(x); // f might fail, but I forgot
> int z = g(y); // g might fail, but I forgot
> return h(z);
> }
> ```
>
> If for some reason, I have forgotten that f() might fail (and signal
> failure), does function g() get called? In case of exceptions no, because
> if f() throws, then g() is never called. In case of outcome<>: no, because
> f() returns `outcome<int>, so the above will fail to compile, an I will be
> forced to rewrite function job().
>

If you choose to write this code, in Noexcept you will get an assert. Even
in NDEBUG builds, let's not forget that if f() fails, y is invalid and g()
should reject it (think of it as e.g. fread getting a 0 for its FILE *
parameter).

Perhaps your point is that ideally f() shouldn't return int but a different
type with special semantics. Okay.

OTOH let's say you're returning a file descriptor. I'd think that
optional<int> is an overkill in this case, in fact you're probably
obfuscating the fact that the int is a FD -- what does it mean if you get
-1 FD in an optional<int> (or for that matter in an outcome<int>)? It's
redundant, and after all it's a safe assumption that you won't get silent
failures from functions if you pass -1 for the FD.

That said, Noexcept works great with optional, too:

int job(int x)
{
  optional<int> y = f(x); // f might fail, but I forgot
  int z = g(y); //if g takes int, you'll get a compile error, just like
with Outcome
  return h(z);
}

(if you did y.result() it'd throw in case of failures (optional<> or not),
so if you call .result() you can't accidentally pass the bad value to g.)


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk