Boost logo

Boost :

Subject: Re: [boost] [outcome] High level summary of review feedback accepted so far
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2017-06-02 06:45:31

2017-06-02 2:06 GMT+02:00 Gavin Lambert via Boost <boost_at_[hidden]>:

> On 2/06/2017 06:33, Niall Douglas wrote:
>> On 01/06/2017 00:35, Gavin Lambert wrote:
>>> On 1/06/2017 08:17, Niall Douglas wrote:
>>>> The programmer, when working with outcome<T>, knows that calling
>>>> .exception() will return the exact same exception as would be thrown
>>>> when calling .value(), thus saving wrapping .value() in a try...catch
>>>> just to capture the exception_ptr emitted.
>>> Except empty. value() throws that as an exception. exception() also
>>> throws that as an exception (not returning it), which means that the
>>> programmer can't make that assumption you just claimed.
>>> Maybe that's what you want (as it leads very quickly to empty ==
>>> std::terminate), but it's not consistent with use of empty as a success
>>> return as you've suggested in a few places.
>> Empty is supposed to have the severest, most abnormal default actions.

> But it means that in a noexcept method, it's unsafe to call exception()
> without a try-catch, even though it would be safe to call in any case other
> than empty. And it means that exception() doesn't return the exception
> that would be thrown by value() in that case either.
> Maybe the answer is "so check for empty first!" but that seems like
> clutter if you're not expecting to see empty but still need to put it in to
> defensively avoid a std::terminate.

There is a reasonable mental model behind it. But you have to forget the
other example (where Niall is using `result<T>` as though it were a

Here is the model: a function that produces `outcome<T>` is allowed to use
the empty state *temporarily*, but is required to put it to either
`has_value` or `has_exception` (or `has_error`) before it returns the final
value. Otherwise it is probably a bug.

The consumer function that obtains the `outcome<T>` can assume that it is
not this empty state.

This is similar situation to when you are obtaining a `std::shared_ptr` or
a `std::unique_ptr` from some function. You often do not have to put an
if-statement to check if it is nullptr. You trust that if someone decided
to build a shared_ptr, this is in order to actually have some object with
shared semantics.

> And it's bizarre to have empty have super-throw behaviour if the method
> wants to treat it like an optional success value, as you've suggested is
> occasionally useful.
> Why not just have exception() return the empty-state exception that
> value() would have thrown?
> Certainly when using AFIO v2, I find myself writing a lot of
>> afio_function().value();. I really wish it were more permissible to
>> throw from destructors, then if the return were errored and you don't
>> deal with it, it throws, no .value() needed.
> It's legal; you just need to explicitly declare it as noexcept(false) and
> only throw the exception if (!std::uncaught_exception()). Though people
> will probably still yell at you about it.

I would protest for one. You either throw from destructor of you don't
throw from destructor. But I do not accept that you sometimes throw and you
sometimes don't.

But there is a more fundamental objection to it. Unless you use destructors
to perform significant tasks and business logic, the normal usage of C++
classes is that other member function do the business logic, and
destructors only clean up resources. If you failed to do the business logic
you start the stack unwinding because other people up the stack depend on
your having finished your part of the logic. In contrast, when you have
failed to release resources, people up the stack still consider your
business task as finished and they can work with it, and likely will not
use the resource you are about to waste (and even if they need it, they
will be informed by a throw from a constructor of other resource-handling


Boost list run by bdawes at, gregod at, cpdaniel at, john at