Boost logo

Boost :

Subject: Re: [boost] Outcome v2
From: Emil Dotchevski (emildotchevski_at_[hidden])
Date: 2017-07-11 20:23:29


On Tue, Jul 11, 2017 at 12:43 PM, Niall Douglas via Boost <
boost_at_[hidden]> wrote:

> On 11/07/2017 18:13, Emil Dotchevski wrote:
> > On Tue, Jul 11, 2017 at 1:08 AM, Niall Douglas via Boost
> > <boost_at_[hidden] <mailto:boost_at_[hidden]>> wrote:
> >
> > outcome<handle, std::error_code, std::filesystem::path>
> >
> > ... could return an open file handle on success, or an error code
> plus
> > the failing path on failure.
> >
> >
> > The third parameter, "error info", should not be specified by the error
> > reporting code because what info is relevant to a failure depends on the
> > context, which is not know an the point you report the error.
>
> That *may* be the case, but it is not always the case.
>

When is it not the case? A trivial program that doesn't need error handling
library. Otherwise, the low level library that reports the error can not
know what is _needed_ in that program to handle the error. At the point of
reporting, all you can do is get things going, but most of the relevant
context comes in higher level functions.

And, importantly, the data that comes from the context in which the failure
is reported does _not_ depend on the failure. So, it is critical to allow
such data to be associated with _any_ error.

> In the case of say a file rename operation, it's very reasonable to
> supply error context info of the two paths involved. Like
> std::filesystem_error does. For such a rename() function, sure you'd
> hard code the error info type.
>
> > As you saw on SG14, using any black box library routines is always
> going
> > to be a problem for the fixed latency user base.
> >
> >
> > What is a black box library routine? If you're referring to the use of
> > TLS, like I said in that thread, it's a constexpr constructor so there
> > is nothing to initialize and no reason for the TLS to be slow. Nobody on
> > SG14 challenged this point, so I'm not sure what you're referring to.
>
> I think that nobody *bothered* to challenge you. That's very different.
> They indicated misgivings with that design choice, you did not
> acknowledge them in a way they felt indicated you were open to being
> corrected, so they said nothing. Happens all the time on boost-dev too
> of course.
>

I think they, like you, can't point at a particular problem but generally
distrust it and so they remain silent. Regardless, most of the arguments
I'm making are about semantics, not speed.

In Noexcept TLS use is means to an end, that end being moving the error
object out of the critical path, because the error is almost exclusively of
concern only to the code that reports it and the code that handles it.

> Personally speaking, I actually don't know in truth. I just feel deep
> suspicion. TLS used to be awful unpredictable latency some years ago,
> only Windows's TlsAlloc() was sane, and it only had 64 slots for the
> entire process which was easy to exceed. But C++11's thread_local has
> forced significant improvements. I know what those are in theory, but
> I've done no deep dive into individual, specific implementations. I'm
> pretty sure most, if not almost all, on SG14 are in the same boat. C++
> 11 thread_local implementations are too new yet.
>
> A really great CppCon talk topic would be benchmarking and poking with a
> stick the three main thread_local implementations. Easily a full hour.
>

Yes, however consider that the general problem of implementing TLS that
requires dynamic initialization and whatnot is a lot more complex than what
Noexcept needs. Literally, to make Noexcept work, you need a piece of TLS
memory where one pointer is set to zero. This is not a tall order, though I
admit that a general solution might do it poorly.

> > Your library is shorter
> > than mine, but it calls unknown latency routines. Mine never does.
> >
> >
> > That was addressing your offer to use Outcome as a "building block" for
> > Noexcept. The point is, the "building block" is heavier than the
> > "product". :)
>
> More lines of code has little to do with heaviness. I got some flak off
> Reddit regarding Outcome v1's length in line count. Totally irrelevant
> to compile time load.
>

Agreed. I still don't think it's serious to suggest that it makes sense to
implement Noexcept in terms of Outcome. :)

> > You also impose TLS on your end users. Mine is much lower level than
> > that, if end users wish to combine mine with TLS to achieve your
> > library, they'll find it very easy.
> >
> >
> > It's the other way around, actually. The use of TLS moves the error
> > objects off the critical path and removes the coupling between errors
> > and error-neutral contexts. These are Good Things.
> >
> > The decision to force users to enumerate their error types (recall again
> > exception specifications) and to move errors up the call chain one level
> > at a time should be justified and supported with data showing that
> > thread_local is too costly in practice.
>
> You're forgetting one of the primary reasons to use an Outcome/Expected
> type is deliberately encode failure handling near the point of failure.
> People specifically want to see the handling code there so it can be
> audited and writing it cannot be moved elsewhere. So you want those
> error types in there to force the issue.
>

This use case is supported by TLS storage as well, however in my experience
the essence of this use case is that the program detects the error, logs it
and continues, there is really no recovery or handling.

This is legit, but in this case you need a good logging library more than
you need a good error handling library (the only other variation of "I'm
dealing with the error right now" is to translate it to another error,
which is always a bad idea.)

> > And the thing is, there is no reason for it to be costly. If it helps,
> > think of it as the refcounting support shared_ptr requires: we can call
> > it tricky, but it is not a problem. Worst case, Noexcept has to
> > implement it if it turns out that the built-in support on some platform
> > is inefficient.
>
> I remain unconvinced.
>
> I'm still strongly of the opinion that if you want C++ exception like
> semantics, turn on C++ exceptions support.

I agree. Noexcept is needed when you can't do that for some domain-specific
reasons or because the code you're dealing with is not exception-safe (I
have seen a lot of hand-waving that domain-specific reasons exist in
practice, but I have never seen hard data to back that up; and dealing with
code that is not exception-safe is pretty common.)

Anyway, what you call exception-like semantics in this case is the ability
to efficiently transport arbitrary types. The reason that is important is
because otherwise you need to translate (bad) or use exception_ptr. The
problem with using exception_ptr is that it does require an allocation, and
there is no way to interrogate it about its content. From where I stand,
Outcome does not solve the most important problem that needs solving by an
error handling library.


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