2017-06-02 2:06 GMT+02:00 Gavin Lambert via Boost
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 `boost::optional<T>`). 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 class). Regards, &rzej;