Boost logo

Boost :

Subject: Re: [boost] Outcome/expected/etc/etc/etc
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2017-06-06 09:04:46


>> I do feel considerable concern with your idea of using the exception_ptr
>> as a payload for the error code. I appreciate the use case for the
>> Filesystem TS, but I feel the fact that the Filesystem TS returns more
>> information via the exception throw mechanism than via the error_code
>> mechanism to be an awful mistake. Some other mechanism should have been
>> chosen to make both of equal information returning value instead of
>> splitting the two and making the error code returning overloads inferior
>> like that. After all, the latter are the natural fit for a Filesystem
>> library. The former are inappropriately heavyweight.
>>
>> What I could live with though is this synopsis:
>>
>> template<class T, class P = shared_ptr<void>, class EC = error_code,
>> class E = exception_ptr> class outcome
>> {
>> T _value;
>> EC _error;
>> union
>> {
>> P _payload;
>> E _exception;
>> };
>> };
>>
>> I personally really wish that shared_ptr could be constructible from an
>> exception_ptr as they surely are implemented the same way on any
>> standard library implementation I could think of, but at least the above
>> correctly lets payload be any type-erased shared_ptr, which is the
>> correct and appropriate type for returning type-erased payload unlike
>> exception_ptr.
>>
>> The question now becomes this: surely the Filesystem TS is cleaner if
>> when returning an errored outcome it supplied a shared_ptr<pair<path,
>> path>> instead of the exception_ptr to the filesystem_error that would
>> have been thrown? Advantages:
>>
>> 1. I would also be fairly sure, without having benchmarked it, that
>> make_shared<pair<path, path>>(path1, path2) will be many times faster
>> than make_exception_ptr(filesystem_error(path1, path, ec)) on most
>> standard library implementations.
>>
>> 2. make_exception_ptr(filesystem_error(path1, path, ec)) will return a
>> null pointer if C++ exceptions are disabled on at least libstdc++, and
>> thus your proposal would be a problem for those users running with
>> exceptions off.
>>
>> What do you think?
>>
>
> I will not address your question directly; but let me offer a remark.
>
> It was my understanding that `error_code_extended`, along with its ring
> buffer usage, was provided to address exactly this problem: provide
> additional payload to `std::error_code`. The current exercise with adapting
> the Filesystem TS can be considered a test of the usefulness of
> `error_code_extended`.

With result<T> which is T|error_code_extended, then yes
error_code_extended can be used to optionally transport a *fixed* set of
payload. By its nature, you get storage for one short string currently a
maximum of 191 characters.

With outcome<T>, we are storing an exception_ptr anyway in this proposed
simplified non-variant design, so I am suggesting go ahead and union
that with a shared_ptr, that way you can optionally supply arbitrary
type-erased payload.

> Some questions arise that i will surely ask in the second review of
> Boost.Outcome (I hope we will have one):
>
> 1. Can `result` be used to successfully replace error handling in
> Filesystem TS?

Not entirely. outcome<T> as proposed above can. The problem is the *two*
paths which Filesystem may return as part of filesystem_error. And each
of those paths could be 16Kb or more.

> 2. If I carry arbitrary payload with a `result` do I need the ring buffer
> for anything?

Sorry, this misunderstanding is the fault of me cutting out some
exposition. Let's do it out in its entirety:

```
template<class T, class EC = error_code_extended> class result
{
  T _value;
  EC _error;
};

template<class T, class P = shared_ptr<void>, class EC =
error_code_extended, class E = exception_ptr>
class outcome : public result<T, EC>
{
  union
  {
    P _payload;
    E _exception;
  };
};
```

So result<T> retains its trivial copy, move and destruction which is
important for optimisation. outcome<T> always forces observable
behaviour because of the potential atomics in exception_ptr, so we lose
nothing by potentially making the same storage alternatively a shared_ptr.

As mentioned above, the shared_ptr possibility means the Filesystem TS
now works equally well with C++ exceptions disabled if that TS used
outcome<T>.

Thoughts?

Niall

-- 
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

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