Re: [boost] [review] Review of Outcome v2 (Fri-19-Jan to Sun-28-Jan, 2018)
2018-02-01 19:13 GMT+01:00 Emil Dotchevski
On Thu, Feb 1, 2018 at 2:51 AM, Andrzej Krzemienski
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: https://wandbox.org/permlink/5vICxckvVG6QO0n1
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.
Indeed.
Outcome helps you avoiding exception handling, so it assumes you can freely use result
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. Regards, &rzej;
participants (1)
-
Andrzej Krzemienski