Boost logo

Boost :

Subject: Re: [boost] Boost.Outcome review - First questions
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2017-05-24 16:12:17


Le 24/05/2017 à 17:20, Niall Douglas via Boost a écrit :
>>>> IIUC, what you want is to have outcomes that copy/move well each one on
>>>> each other.
>>> That was exactly a motivating reason for choosing this design
>>> originally. Having the same base storage implementation should allow the
>>> compiler to completely eliminate memory copying to implement layout
>>> changes when a personality is changed.
>> How outcome ovoids this copying?
> Compilers spotted the common outcome::value_storage during moves when it
> was identical, and totally eliminated any memory copies.
Okay, so the single thing outcome::outcome provides is the conversion
from the other Outcomes.
I believe this is great and I will add it to the open question for
std::expected as I've already notified privately to you (waiting a
response :) )
>>>> Is this the major drawback you find to the use of
>>>> std::expected and std::optional?
>>> No, not at all. Very recent optimisers e.g. in better than clang 4.0 and
>>> in GCC 6.0 with -O3 optimisation turn out to have rendered the common
>>> storage design choice no longer appropriate. But that wasn't the case
>>> when I began Outcome two years ago.
>> And then?
> clang 4.0+ and GCC 6.0+ with -O3 optimisation appear to now be clever
> enough to spot trivial move constructors and trivial types, and not copy
> memory when the bits in memory would not change.
Do you mean that now we can use optional and expected without any
performance penalty and that the design of Outcome is not needed any more?
>
> I would therefore expect that your reference Expected implementation
> would now be identical or very similar in runtime performance to Outcome
> on clang 4.0+ and GCC 6.0+. MSVC does, of course, still have some way to
> go yet, but VS2017 is a world of improvement over VS2015.
I was looking for any advantage of the design of Outcome ;-)
>
>>> You would thus exchange (slightly) worse performance of successful code
>>> in exchange for vastly better and predictable performance of
>>> unsuccessful code.
>>>
>>> Note that the cost of throwing and catching a C++ exception with a table
>>> based EH C++ compiler is highly unpredictable, and costs between
>>> 1500-3000 CPU cycles per stack frame unwound between the try and catch
>>> site. You can find the benchmark in the benchmark directory in Outcome's
>>> git repo.
>> So you are comparing to the use of exceptions here and not to the use of
>> output error codes?
> I am comparing the cost of throwing and catching exceptions to returning
> an expected with unexpected state.
Throwing an exception should be rare, exceptional, and most of the cases
we could ignore them.
expected is there to return expected values and possible errors, but
these error are not necessarily exceptional and that could occur more often.
I will never change a function that return TR and that can throw
bad_alloc for a function that return expected<T, bad_alloc>. This is
exceptional.
I will not return expected for a function that could exceptionally
detect that there is a hard disk error. This is exceptional.
expected should cover the expected errors not the unexpected ones.
Exceptional case should continue using exceptions.

I don't want to transport this kind of exceptional situations in my
interface.
If the user cannot use exceptions for whatever reason, it could use
expected to transport this exceptional situations as well, but IMO the
exceptional situation will result in terminate in most of the cases.
>
>>>> What is the behavior on the absence of exception on the functions that
>>>> throw exceptions? Are these functions disabled? Does the fucntion
>>>> terminate? Calls a handler?
>>> All exceptions Outcome ever throws are done via a user redefinable
>>> macro. Those macros are listed at
>>> https://ned14.github.io/boost.outcome/md_doc_md_04-tutorial_c.html.
>>>
>>> If C++ exceptions are enabled, the default macro definition throws the
>>> exception.
>>>
>>> If C++ exceptions are disabled, the default macro definition prints a
>>> descriptive message and a stacktrace to stderr and calls
>>> std::terminate().
>> This is similar to what BOOST_THROW_EXCEPTION does, isn't it?
>> Why re-invent the wheel?
> End users can define the Outcome exception throwing macros to call
> BOOST_THROW_EXCEPTION if they wish. For standalone Outcome users, the
> default behaviour is minimal, but sufficient.
Boost users should have already a default for BOOST_THROW_EXCEPTION and
shouldn't need to add any additional customization for what to do when
exceptions are not enabled.

I don't agreed with the change on BOOST_THROW_EXCEPTION done by Emil,
but the reality is that it is there now. Users of Boost should not
customize each library, when there is already a common mechanism.
>
>>>>> is an
>>>>> excellent neighbour to all other C++ libraries
>>>> as for example?
>>> It pollutes no namespaces, interferes in no way with other C++ including
>>> other versions of itself. You can mix multiple versions of Outcome in
>>> the same binary safely.
>> There are several version of Outcome?
> Currently every time I commit to the git repo there is a new version of
> Outcome. The ABI is permuted with the SHA of the git. This prevents
> unstable versions of Outcome conflicting with different unstable
> versions of Outcome in the same process.
Hugh. I don't see how would someone like to use two unstable version of
the same library.
>
> I will, at some future point, declare a stable ABI and pin it forever to
> v1 of the ABI and have that checked per commit by Travis.
I don't see any added value to this versioning. Up to you.
>
>>>>> AND build systems,
>>>> I'm less concerned by the build systems, but could you elaborate?
>>> Outcome doesn't require any magic macros predefined and can be used by
>>> end users simply by dropping a tarball of the library into their source
>>> code and getting to work.
>> While this could be a quality for a standalone library, this is not a
>> quality for a Boost library.
> The other C++ 14 library in Boost does not require any of Boost either.
Oh, yeah I forgotten, Boost.Outcome is a C++14 library and so it cannot
use Boost and reinvent the wheel ;-)
>
>>>>> (And my thanks to Andrzej for the motivating example on the landing
>>>>> page
>>>>> of the docs, he did a great job capturing the universal error handling
>>>>> framework aspect of Outcome. I would love to know if reviewers can make
>>>>> sense of it, or did it just confuse the hell out of everybody)
>>>>>
>>>> Could you tell us which example?
>>> It's the landing page motivating code example. Bottom of
>>> https://ned14.github.io/boost.outcome/index.html.
>>>
>> No this example doesn't show any advantage of Boost.Outcome, quite the
>> opposite. However the other example that Andrzej has posted recently is
>> a better example as I have already said as a response.
> The only difference was returning result instead of outcome, and
> removing the exception throwing function call. Otherwise the examples
> are identical.
>
The return value is very important; the example show an important thing
of Boost.Outcome: error propagation.

Vicente


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