
On Mon, Jul 10, 2017 at 5:58 PM, Niall Douglas via Boost < boost@lists.boost.org> wrote:
Are you confusing result and outcome?
result
outcome
The default template parameters give exactly the same as with v1. But if say you chose `result
` or `outcome ` then that works too. I still don't understand what does it mean to use void. I don't understand what this means:
"outcome
would equal outcome " The default declaration for outcome is:
``` template < class R, class S = std::error_code, class P = std::exception_ptr, class NoValuePolicy = policy::default_outcome_policy
requires (std::is_void<EC>::value || std::is_default_constructible<EC>::value) && (std::is_void<P>::value || std::is_default_constructible<P>::value) class [[nodiscard]] outcome; ```
Yay the Concepts TS. Anyway, therefore `outcome
` equals `outcome `. Does it make sense now?
No. Why would I pass void for S or P?
By the way, such policy-based design is probably not a ideal for
error-handling library return types, for the same reason shared_ptr<T> is
better than Alexandrescu's policy-based smart pointer, and for the same
reason function<T> (as it is now standardized) is better than function
Take this for what it's worth, but the earlier outcome design was a lot
more focused.
You may be forgetting my initial claims of a "multi-modal" design. v1 wore many hats. v2 has no head, so it cannot wear a hat.
I'm afraid my reading is that you're shifting important design decisions to the user, who has (by definition) lower qualifications in the domain of error handling. Flexibility is not always good, after all we all have a C++ compiler and can handle errors with all the flexibility we need -- without an error handling library.
Reading the documentation, some of it sounded like evangelization of std::error_code, so the conclusion I drew was that the idea is that you don't specify the error types because you should only be using std::error_code, except that in the real world you might get an exception from somewhere, in which case outcome lets you stuff a std::exception_ptr into it too. That makes sense, even if I think that it is not practical to assume that everyone will jump on the std::error_code train.
That was purely a simplifying narrative which was taken due to continuing Reddit confusion. v1 always could do far more that the tutorial suggested. You could, in fact, customise any of the types to anything you liked so long as you met an error_code or an exception_ptr contract.
I know. That makes sense, it's crystal clear.
v2 now concepts matches instead. If you feed it a type matching an error_code concept, it treats it as an error code, otherwise it does not. Same for exception_ptr. Thus `outcome
` is legal, but unusable, because you cannot construct one without resorting to UB.
Assuming we agree that passing int for the P makes no sense at all:
1. Could you show a practical case that illustrates the need for using
anything but std::exception_ptr for P?
2. What is the benefit of P being semantically different from S? In other
words, what is the difference/benefit of outcome
On the other hand, expected
(Vicente) says no, std::error_code is not the only option so this should be a template parameter. On top of that, expected (Peter) says well, at the very least you might get an exception from somewhere so you have to be able to have e.g. expected , so we'll take more than a single E. v2 was designed to dovetail into Expected neatly. It's basically a hugely simplified and thus much faster to compile subset. That should allow Expected to take on much more monadic stuff, if Vicente prefers.
The other significant difference you introduced post-review was that outcome no longer had strict value-or-error semantics. This too helped set it apart.
In the default configuration, outcome
is only strictly value-or-error, value-or-exception, or value-or-error+exception.
What other options are out there? What could I do with a struct with 3 members, which outcome "strictly" forbids?
But now, and it may be just me, but I am confused. What exactly is the difference between outcome and the two flavors of expected we have? Is it essentially the same as expected
, except that in outcome the strict value-or-error semantics (that you may remove later) are optional? It's a low level subset. Struct-based storage, not variant-based. Fast. Lightweight. ABI stable. But not rich, it's a barebones type.
Are you claiming that outcome
This might be reasonable if it is a one-off thing, but consider that the general case is a bit more complicated, possibly involving different compilation units, and you do need to ensure that when the thread terminates "state" doesn't contain an error. It may or may not make sense to add something to that effect to Outcome.
v2 is designed to be subclassed into localised implementations in a local namespace, which is a new thing. So it's very easy to add additional behaviours and features to your localised implementation, whilst retaining the cross-ABI interoperability between many localised implementations.
v2 only provides the raw building block. What you do with it after it totally up to you.
The question is what's the value in using Outcome as a building block? Why would anyone bother, if using Outcome they're basically free to do whatever?
For example, one could build your Noexcept library with it quite easily, and thus "plug in" to Expected, P0650 etc.
I think Noexcept is a lot more lightweight than you think. The internal machinery it is based on (what I think you imagine your building block would replace) is about 500 lines, out of about 800.