
On Tue, Jul 11, 2017 at 12:43 PM, Niall Douglas via Boost < boost@lists.boost.org> wrote:
On Tue, Jul 11, 2017 at 1:08 AM, Niall Douglas via Boost
mailto:boost@lists.boost.org> wrote: outcome
... could return an open file handle on success, or an error code
On 11/07/2017 18:13, Emil Dotchevski wrote: 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.