Boost logo

Boost :

Subject: Re: [boost] [review] Review of Outcome v2 (Fri-19-Jan to Sun-28-Jan, 2018)
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2018-02-01 19:04:25

2018-02-01 19:13 GMT+01:00 Emil Dotchevski <emildotchevski_at_[hidden]>:

> On Thu, Feb 1, 2018 at 2:51 AM, Andrzej Krzemienski <akrzemi1_at_[hidden]>
> wrote:
>> Boost.Outcome solves the problem decently: you get a compiler error or a
>> compiler warning
> Do you? What if you have:
> result<void> foo();
> void bar()
> {
> foo();
> }
> What compile error will that generate?

"ignoring returned value of type 'outcome_v2_f1696316::result<void>',
declared with attribute nodiscard [-Werror=unused-result]"

online example:

> Now, in Boost.Noexcept, if the programmer forgets to test for the failure
>> (with `try_`), the subsequent function is still executed with the default
>> value of type `T` returned from the funciton that failed. Because in
>> Boost.Noexcept when function fails it returns a default-constructed `T`.
> The idea in Noexcept is that since the function _must_ return something if
> there is an error, it returns throw_return<T>() but that is not automatic;
> the user still has to say e.g. return throw_(error()).

I understand the constraints you are under. I also acknowledge that this is
the best one can do. Nonetheless, I still see the limitation of the
solution. If a third party function is returning a type representing a
resource, and this type does not provide a default constructor, there is no
way to "throw_". You cannot even customize `throw_return<T>()` because what
value would you return? You are reporting failure because you cannot
acquire the resource. And because it is a third-party interface, you cannot
adapt the type. So, in the end, there will be functions where you cannot
apply Boost.Noexcept.

>> And when you check it systematically it is all ok, but then you forget
>> (and this is a tedious task), you violate the contracts. No compile-time
>> check, no run-time check.
> In Noexcept there _is_ run-time check: if noexcept_::has_current_error()
> is true at the time a thread terminates, it invokes std::terminate(). Not
> ideal, and won't take action if the thread never terminates -- and it may
> never terminate because, like in Outcome, if the caller doesn't check at
> every function call, the program keeps going.

I was imprecise. you do have a run-time check, but it comes too late: it
does not prevent functions to be called with failed preconditions.

>> I consider this a serious issue.
> It is, programmers forget to check for errors all the time.

And Boost.Outcome helps detect these situations at compile-time (of course,
at the expense of controlling the return type).

>> And taking into consideration the advantages of Boost.Noexcept you
>> mention, I can only conclude that Boost.Noexcept is superior to
>> Boost.Outcome in one aspect but inferior in another.
> The two libraries solve different problems.


> Outcome helps you avoiding exception handling, so it assumes you can
> freely use result<T,E> throughout your interfaces.

Well, it can help you avoid exceptions. But it also helps you deal with the
lack of exception handling. As you say, the trade-off is that you use
`result` in return type.

> Noexcept helps you deal with a lack of exception handling, so it assumes
> that at least some of the interfaces are beyond your control, and doesn't
> impose a return type.

Yes, here the trade-off is, no altering of the return type, but unsafe
consequences when you forget to check for "thrown" exceptions. And there
must exist a "spare" value in the returned type.

There are benefits to using a special return type (to protect against using
> an invalid value), but Noexcept leaves that to the user because return
> values don't transport errors, just the T; you could e.g. use
> std::optional<T>.

And user may not be able to make this decision because he may not be in
control of the return type.


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