Boost logo

Boost :

Subject: Re: [boost] Noexcept
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2017-06-13 07:25:48


2017-06-12 23:26 GMT+02:00 Emil Dotchevski via Boost <boost_at_[hidden]>
:

> 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:
> > >
> > >
> > > 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,

At which point? Where is the assertion located?

> 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).
>

I absolutely agree that it is wrong to pass an invalid `y` to `g()`, I
think we are just disagreeing on who is responsible for preventing this
from happening.

When I write function `job` (from the example above), I should make sure
that it does not happen. But I might simply forget about it for some
reason. The question now is: does the framework help me if I forget?

In case of C++ exceptions, the answer is "yes": the call to `g()` is
automatically skipped if I forget.

In case of outcome<>, the answer is "yes": the compiler refuses to compile
the code.

You say that in case of Noexcept, there is an assertion, but I fail to see
where you could put this assertion, so that a call to `g()` is prevented.
Maybe you could elaborate?

>
> 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.
>

A valid concern. A solution to that has been provided by Vicente: just use
type `uncertain<T>`, which is just a `T` inside, but because it is a
different type, you cannot silently use it in place of `T`. But this would
compromise your other goal: that some functions want to remain
exception-neutral.

C++ exceptions can both prevent invalid invocations of `g()` and remain
exception-neutral. This is fine. `outcome<>` prevents invalid invocations
of `g()` but compromises exception neutrality (still fine by some
standards, and sometimes desirable). In case of Noexcept, you provide
neutrality, but compromise the guarantee that invalid invocations of
`g()`are prevented. This might be considered a wrong trade-off.

Regards,
&rzej;


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