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-03 14:05:51
2018-02-03 1:20 GMT+01:00 Niall Douglas via Boost <boost_at_[hidden]>:
> > So, my vote is based on (in no particular order):
> > 1) In these discussions, I can clearly recognize the dislike for
> > exception handling and even C++ (I don't mean by you personally) that I
> > have been exposed to in the past, since for years I've been surrounded
> > by people who falsely believed that they can't afford exceptions or
> > smart pointers or proper serialization, and they have strong, if
> > incorrect, opinions on what's wrong with C++. I believe that this
> > attitude does not belong to Boost. It's possible that I got this wrong.
> > It may be interesting to know how many of the current users of
> > "standalone" Outcome use Boost in "low latency" environments or at all.
> > Do you have an idea?
> No, nor do I think it matters. The rationale to use Outcome for me in my
> code is to let the caller decide whether an exception should be thrown
> or not, rather than the function experiencing the failure hard coding an
> exception throw which invokes an unavoidable table search I'd like to
> avoid. That's my primary use case in my own code.
But look like your rationale is different than the motivation section in
the documentation. Maybe this is a communication problem. Again we may have
failed in communicating to the people what this library is not.
> My secondary use case is that rather than encode the logic for how to
> type erase/rethrow/convert the failure into an exception throw via a
> preprocessor macro which is how it's usually done e.g.
> BOOST_THROW_EXCEPTION(), I'd like to set rules via the type system for
> what is to happen. No macros needed. The policies are very useful for
> this use case.
But there is one notable inconvenience. Two different policies applied in
two different places render two different instances of `result<T>`. These
instances are not inter-convertible (or am I wrong) and you cannot use them
freely together. No copy elision. I didn't mind them in the review because
I concluded I could always go with the defaulut one. One you have added the
narrow-contract `assume_value()` I no longer mind that `value()` throws,
and I would never have any business in changing the defaults.
> My tertiary use case is accumulation of unknown failures into my own
> test framework. Failures can come from anywhere, including the STL, and
> I preserve all the original information. No information loss at any
> point, and a complete chain of execution is recorded by the test
> framework which can be checked for correct handling of failure. The 16
> bits of spare storage in Outcome combined with the ADL construction
> hooks and TLS is very useful for this use case.
> Now, lots of people have other use cases for Outcome, but those three
> are my main personal motivating use cases. And Outcome is a superb
> solution for my needs, else I'd not have invested so much effort into it.
> > 2) Clearly, Outcome _does_ want to help pass errors across API
> > boundaries, including in generic contexts. The problem is that
> > result<T,E> compute() noexcept;
> > is very similar to
> > T compute() throw(E);
> > (yes, I know exception specifications are enforced dynamically, but
> > that's not what's wrong with them, see the second question here:
> > https://herbsutter.com/2007/01/24/questions-about-
> > My reasoning is that if with Outcome you can always return the exact
> > error type you've specified in your static interface, the same approach
> > would work for (perhaps statically-enforced) exception specifications.
> The exception specification analogy doesn't apply to Outcome. What
> doomed exception specifications is indirect function calls combined with
> the side channel exception throws operate through, so your function
> which guarantees to never throw anything but E happens to call some
> overriden virtual function which throws a different type, and boom
> you've just called std::terminate.
> That's because exception throws operate via a side channel outside the
> normal flow of execution. Outcome doesn't have that - it returns via the
> normal flow of execution. Therefore we can hard guarantee that if the
> program compiles, the "exception specification" is met. Overriding a
> virtual function will only compile if the return type matches
> result<T,E>. So overrides can't introduce hidden calls to
> std::terminate. Therefore exception specifications are the wrong analogy
> for result<T, E> returning code.
I think what Emil meant is that if one library you use reports `result<T,
E1>` and another reports `result<T, E2>` you do not have a good type to
return from the code that combines the two. Unless you apply a translation,
but this way you loose information.
> > Logically, to address this concern you could:
> > - Demonstrate that there is a major flaw in my analogy, or
> > - demonstrate that exception specifications could be made practical,
> > including in generic contexts, possibly by using some clever
> > policy-based design, or
> > - provide an interface that can forward arbitrary errors ot the caller.
> > (I see these as mutually-exclusive).
> As I pointed out to you during Outcome v1 review, you can implement TLS
> push and pop for Outcome just the same as your Noexcept library does.
> As an example, in AFIO, all errored results are recorded into a TLS
> ringbuffer which tracks the execution log, so for any given error, AFIO
> can tell you the exact sequence of API calls, including to internal
> functions, and their parameters, leading up to that point. It also can
> tell you exactly how AFIO handles the failure, every single function
> called, including internal ones, as the stack is unwound. I haven't
> implemented it yet, but it'll all designed to get fired into a file so
> it acts as an i/o validation audit log, but right now its main use is
> for validation testing, so the test suite can say if the correct
> execution paths were taken for some given failure scenario.
> Outcome doesn't come with such features builtin. But it provides a rich
> set of customisation points to let you roll any bespoke error and
> exception handling framework you like. It really is very handy to have
> in the toolbox, abstracting out the handling of failure into a generic
> framework is something we've not done much work on in C++ in recent years.
> ned Productions Limited Consulting
> http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
> Unsubscribe & other changes: http://lists.boost.org/
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk