[outcome v2] Please comment on new result<T, EC> reference API docs
Dear Boost, I've been hard at work refactoring Outcome to match the peer review a few weeks ago. Much comment at the time mentioned the poor experience of the reference API docs, so can I get a community check of this: https://dedi4.nedprod.com/static/result-v2/doc_result.html This was generated by the new C++ reference docs generator https://github.com/foonathan/standardese. You'll no doubt spot quite a few formatting bugs, Jonathan says he's onto them. But the usage of libclang rather than doxygen's broken C++ parser clearly shows a lot of benefit, it's just standardese still remains quite a lot short in terms of necessary features to make output fully correct. I won't give the changelog for Outcome v2 result<T> over Outcome v1 yet. Let's see how you fare with the above reference API docs first. But as you'll note, there is considerable change over before as we now slot into a niche not occupied by Expected nor EitherMonad, indeed this now looks much more like Robert's or Lawrence Crowl's slimmed down object. This result<T> is approx 0.6 CPU cycles per stack frame unwound slower which I feel is acceptable in exchange for no more variant storage, which in turn released lots of SFINAE budget for me. (FYI v2 outcome<T> isn't done yet, but it will extend v2 result<T>) Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Niall Douglas wrote:
Dear Boost,
I've been hard at work refactoring Outcome to match the peer review a few weeks ago. Much comment at the time mentioned the poor experience of the reference API docs, so can I get a community check of this:
Ideally, one would want only result<> documented, with all its public members, without the implementation detail result_base<>. Not sure if this is possible with standardese at the moment.
I've been hard at work refactoring Outcome to match the peer review a few weeks ago. Much comment at the time mentioned the poor experience of the reference API docs, so can I get a community check of this:
Ideally, one would want only result<> documented, with all its public members, without the implementation detail result_base<>. Not sure if this is possible with standardese at the moment.
It is not possible with standardese at the moment. But before any second review, I'd manually copy and paste result_base<> into result<> so it looks right. More importantly though, is that reference API documentation what people were looking for? With clauses for effects, requires, throws etc? Do I need to write more detail for the APIs, or is what is there enough? Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Niall Douglas wrote:
More importantly though, is that reference API documentation what people were looking for? With clauses for effects, requires, throws etc?
Yes, this is the style the C++ standard uses.
Do I need to write more detail for the APIs, or is what is there enough?
You could be more detailed in places. For example,
""
Function template outcome::policy::throw_directly::narrow_value_check
template <class Impl>
static constexpr void narrow_value_check(Impl* self) noexcept;
Performs a narrow check of state, used in the assume_value() functions
""
You have to say what happens, that is, you're missing the Effects clause
here.
Another minor comment:
noexcept(noexcept(value_type(std::forward<T>(t)))&&noexcept(error_type(std::forward<U>(u))));
This is usually written as
noexcept(is_nothrow_constructible_v
Do I need to write more detail for the APIs, or is what is there enough?
You could be more detailed in places. For example,
"" Function template outcome::policy::throw_directly::narrow_value_check
template <class Impl> static constexpr void narrow_value_check(Impl* self) noexcept;
Performs a narrow check of state, used in the assume_value() functions ""
You have to say what happens, that is, you're missing the Effects clause here.
Great point.
Another minor comment:
noexcept(noexcept(value_type(std::forward<T>(t)))&&noexcept(error_type(std::forward<U>(u))));
This is usually written as
noexcept(is_nothrow_constructible_v
&& is_nothrow_constructible_v );
I was trying to work around an ICE on MSVC :( But it's not working, so I'm putting it back to how you describe.
If you haven't, you could look at the C++ standard to get an idea of how it specifies things, for example here: http://eel.is/c++draft/tuple.tuple
SmartPtr uses a similar style too, f.ex. here:
http://www.boost.org/doc/libs/develop/libs/smart_ptr/doc/html/smart_ptr.html...
I find writing this overspecification highly unnatural to me. It's not how I think about APIs at all. Thanks for your feedback Peter. Good to know this sort of documentation is what people want. I'll keep pressing onwards. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Niall, what does result
On 22/06/2017 00:21, Peter Dimov via Boost wrote:
Niall, what does result
represent?
Same as expected
On Wed, Jun 21, 2017 at 4:58 PM, Niall Douglas via Boost < boost@lists.boost.org> wrote:
On 22/06/2017 00:21, Peter Dimov via Boost wrote:
Niall, what does result
represent? Same as expected
. It's legal to return that failure occurred, but no information as to why. I see that as being possibly useful.
What's far more interesting is result
. Yes expected is also legal too. Where things get mind bendy is that v2 result can return a T with additional info E, so you can quite legitimately have a result with both .has_value() and .has_error() returning true.
Wasn't this one of the issues people took with Noexcept, that since it works with any return type whatsoever, there is no clear invariant, and you could end up in a situation where you have a value and an error? (I actually agree with this criticism, but in my mind this is a small price to pay for not burdening return objects with transporting errors as well as values.)
What's far more interesting is result
. Yes expected is also legal too. Where things get mind bendy is that v2 result can return a T with additional info E, so you can quite legitimately have a result with both .has_value() and .has_error() returning true. Wasn't this one of the issues people took with Noexcept, that since it works with any return type whatsoever, there is no clear invariant, and you could end up in a situation where you have a value and an error? (I actually agree with this criticism, but in my mind this is a small price to pay for not burdening return objects with transporting errors as well as values.)
The idea, since we no longer have variant storage, is to see if we'd like to implement http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0262r0.html (except with the parameter order reversed). Now, I am fairly sure that .error() is the wrong way to return a status. I am minded that a .status() should do that. I just did a major refactor there as yesterday's code made GCC 6 and VS2017.3 barf. I'm on it. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Niall Douglas wrote:
The idea, since we no longer have variant storage, is to see if we'd like to implement
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0262r0.html (except with the parameter order reversed).
I'm not particularly enthusiastic about status_value (and the argument given
in the paper is unconvincing.) Neither am I thrilled by the idea of
result
On 22/06/2017 02:08, Peter Dimov via Boost wrote:
Niall Douglas wrote:
The idea, since we no longer have variant storage, is to see if we'd like to implement
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0262r0.html (except with the parameter order reversed).
I'm not particularly enthusiastic about status_value (and the argument given in the paper is unconvincing.) Neither am I thrilled by the idea of result
(an optional) or result (something like a glorified pair .)
I've fixed the problems you raised at https://dedi4.nedprod.com/static/result-v2/doc_result.html and moved the implementation stuff into a impl namespace so standardese will still show the docs for the composited types, but not be usable by end users.
result
in its original form represented a very clear concept: the type of a function that must either return a value, or a reason for the lack of value. All of the above do not fit this concept and to me ring hollow.
Andrzej feels similarly to you.
But I don't think things are as bad as both of you feel. In v2 result
2017-06-22 4:23 GMT+02:00 Niall Douglas via Boost
On 22/06/2017 02:08, Peter Dimov via Boost wrote:
Niall Douglas wrote:
The idea, since we no longer have variant storage, is to see if we'd like to implement
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0262r0.html (except with the parameter order reversed).
I'm not particularly enthusiastic about status_value (and the argument given in the paper is unconvincing.) Neither am I thrilled by the idea of result
(an optional) or result (something like a glorified pair .) I've fixed the problems you raised at https://dedi4.nedprod.com/static/result-v2/doc_result.html and moved the implementation stuff into a impl namespace so standardese will still show the docs for the composited types, but not be usable by end users.
result
in its original form represented a very clear concept: the type of a function that must either return a value, or a reason for the lack of value. All of the above do not fit this concept and to me ring hollow. Andrzej feels similarly to you.
But I don't think things are as bad as both of you feel. In v2 result
you now can have: 1. Successful: T
2. Failure: EC
3. Successful + Status: T + EC
The reason I don't think this is a problem is because you can't accidentally return a wrong result
in practice. Either you're using result to return Success + Status, or you're using result to return Success | Failure. It is very hard to accidentally write code which does the wrong thing here, almost certainly the type system would refuse to compile incorrect code.
You could say "if you do not like the state `has both value and error`,
then don't create it", and this could be considered acceptable, but now
that you have said you intend to change "error" into "status", you are
affecting even this conservative usage.
In the paper by Laurence Crowl, in the example with queue status:
```
Value queue::value_pop();
queue_op_status queue::wait_pop(Value&);
```
what he is describing is:
pair
You could say "if you do not like the state `has both value and error`, then don't create it", and this could be considered acceptable, but now that you have said you intend to change "error" into "status", you are affecting even this conservative usage.
In the paper by Laurence Crowl, in the example with queue status:
``` Value queue::value_pop(); queue_op_status queue::wait_pop(Value&); ```
what he is describing is: pair
Whereas what you provide (if I got it correctly) is: pair
Which means I can get a value with no status. This still does not implement P0262.
In response to yours and Peter's concerns, I've now tightened the
semantics considerably. For result
So result
is either wearing a "status hat", or it is wearing a "failure hat". It cannot be both. The only remaining objection now is that result<...> is named the same yet has different semantics depending on its S type. Yet error_code is also a status, it's just a *negative* status rather than a *positive* status.
I've pushed clarifying reference API docs regarding this trait separation of positive versus negative status types to https://dedi4.nedprod.com/static/result-v2/doc_result.html. I appreciate that this change is controversial. However, I am also minded that there is no point in Outcome v2 covering the identical ground as Expected, just less well. Outcome ought to be isomorphic to Expected, be strong at the areas Expected is weak at and so on. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Niall Douglas wrote:
I appreciate that this change is controversial. However, I am also minded that there is no point in Outcome v2 covering the identical ground as Expected, just less well.
Can't say I agree with that. Outcome should cover the ground that needs to
be covered. Not cover useless ground that nobody needs covered (status) just
to avoid the "expected" ground. If that makes result
On 23/06/2017 00:00, Peter Dimov via Boost wrote:
Niall Douglas wrote:
I appreciate that this change is controversial. However, I am also minded that there is no point in Outcome v2 covering the identical ground as Expected, just less well.
Can't say I agree with that. Outcome should cover the ground that needs to be covered. Not cover useless ground that nobody needs covered (status) just to avoid the "expected" ground. If that makes result
virtually identical to expected , so be it. This is just a sign that both you and Vicente have the basic design correct. You will still differentiate based on implementation quality, extra features of error_code_extended, and the added value that outcome brings to the party.
I appreciate this.
However I am thinking here in terms of WG21 standardisation,
specifically SG14's work on a std::vector upgrade which doesn't have the
really unfortunate unpredictable latency. The general idea is that a low
latency std::vector would never expand its capacity automatically,
instead it would return success + capacity approaching warning status.
You then could schedule the construction of a new, bigger vector outside
the hot path.
That's why Lawrence's Status + Value proposal garnered such interest,
but there were concerns about whether an expected
Niall Douglas wrote:
However I am thinking here in terms of WG21 standardisation, specifically SG14's work on a std::vector upgrade which doesn't have the really unfortunate unpredictable latency. The general idea is that a low latency std::vector would never expand its capacity automatically, instead it would return success + capacity approaching warning status. You then could schedule the construction of a new, bigger vector outside the hot path.
I find this example as unconvincing as Lawrence's original one. For one, vector functions that grow the size typically return void. So you could just turn that into bool or an enum and you'd be done. For another, when you're in the hot path, the "capacity warning" status is not actionable, so it's of little use. Instead, when you get out of the hot path (or, better yet, before you get into it), you'd simply check capacity(), see if it's yellow/red, and reserve. Or not even check, just reserve( size() + <hot path max size requirement> ).
so the idea was that expected
could be returned.
Sounds like the discussion took place in a pub. Not that there's anything wrong with that. :-)
However I am thinking here in terms of WG21 standardisation, specifically SG14's work on a std::vector upgrade which doesn't have the really unfortunate unpredictable latency. The general idea is that a low latency std::vector would never expand its capacity automatically, instead it would return success + capacity approaching warning status. You then could schedule the construction of a new, bigger vector outside the hot path.
I find this example as unconvincing as Lawrence's original one. For one, vector functions that grow the size typically return void. So you could just turn that into bool or an enum and you'd be done.
For another, when you're in the hot path, the "capacity warning" status is not actionable, so it's of little use. Instead, when you get out of the hot path (or, better yet, before you get into it), you'd simply check capacity(), see if it's yellow/red, and reserve. Or not even check, just reserve( size() + <hot path max size requirement> ).
All valid observations. Yet SG14 likes anything which relocates latency unpredictable things (exception throws, malloc, etc) to the caller. It's certainly how I'd have personally designed the STL originally. std::vector's latency insensitive design has always irritated me, and I still hope - without much basis - that any STL v2 will fix that. I'm minded to try and deliver SG14 a vocabulary type which can help with that so they can propose it for standardisation. As much as it'll cause problems with the Boost review, it delights SG14 members. I received quite a lot of private email during and after the Outcome v1 review about how from the review feedback on boost-dev it was clear here doesn't understand SG14's needs and priorities. I really wish they'd say that here instead of emailing me privately about it. I already know, it's a difference of mindset and philosophy. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Thu, Jun 22, 2017 at 6:13 PM, Niall Douglas via Boost < boost@lists.boost.org> wrote:
However I am thinking here in terms of WG21 standardisation, specifically SG14's work on a std::vector upgrade which doesn't have the really unfortunate unpredictable latency. The general idea is that a low latency std::vector would never expand its capacity automatically, instead it would return success + capacity approaching warning status. You then could schedule the construction of a new, bigger vector outside the hot path.
I find this example as unconvincing as Lawrence's original one. For one, vector functions that grow the size typically return void. So you could just turn that into bool or an enum and you'd be done.
For another, when you're in the hot path, the "capacity warning" status is not actionable, so it's of little use. Instead, when you get out of the hot path (or, better yet, before you get into it), you'd simply check capacity(), see if it's yellow/red, and reserve. Or not even check, just reserve( size() + <hot path max size requirement> ).
All valid observations.
Yet SG14 likes anything which relocates latency unpredictable things (exception throws, malloc, etc) to the caller. It's certainly how I'd have personally designed the STL originally. std::vector's latency insensitive design has always irritated me, and I still hope - without much basis - that any STL v2 will fix that.
If there is anything wrong with std::vector that isn't it. If you want a container that is about as fast as vector when indexing but can grow without reallocation, use deque. If you insist on the elements being sequential in memory, reserve. By the way, even if vectors didn't reallocate, the fact that the elements must occupy sequential memory can become a problem on some platforms for very large vectors, exacerbating heap fragmentation issues. Incidentally, these tend to be the same platforms where the performance impact on reallocations is most problematic. At any rate, such issues are a consequence of language design choices originating in C, and can not be alleviated by "fixing" std::vector.
Yet SG14 likes anything which relocates latency unpredictable things (exception throws, malloc, etc) to the caller. It's certainly how I'd have personally designed the STL originally. std::vector's latency insensitive design has always irritated me, and I still hope - without much basis - that any STL v2 will fix that.
If there is anything wrong with std::vector that isn't it.
There is a significant minority who want their containers to never, ever allocate memory on their own. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Niall Douglas wrote:
All valid observations.
Yet SG14 likes anything which relocates latency unpredictable things (exception throws, malloc, etc) to the caller. It's certainly how I'd have personally designed the STL originally. std::vector's latency insensitive design has always irritated me, and I still hope - without much basis - that any STL v2 will fix that.
I'm minded to try and deliver SG14 a vocabulary type which can help with that so they can propose it for standardisation.
Do you have in mind some outline of how this SG14-friendly, status-based, vector would look like? A few signatures are fine, no need to list the whole synopsis, although it's not that much.
Yet SG14 likes anything which relocates latency unpredictable things (exception throws, malloc, etc) to the caller. It's certainly how I'd have personally designed the STL originally. std::vector's latency insensitive design has always irritated me, and I still hope - without much basis - that any STL v2 will fix that.
I'm minded to try and deliver SG14 a vocabulary type which can help with that so they can propose it for standardisation.
Do you have in mind some outline of how this SG14-friendly, status-based, vector would look like? A few signatures are fine, no need to list the whole synopsis, although it's not that much.
Ah, that's not my field. I've just been monitoring the work of others.
And do remember I'm not on SG14, I can't afford to attend their
meetings, same as WG21 meetings. Most, but not all, of their work can be
found at https://github.com/WG21-SG14.
I can tell you however what I'd personally prefer, though be aware the
below is off the top of my head and not thought through. Personally
speaking, I'd prefer all STL v2 containers to be exclusively made up of
composed views of fixed-at-construction capacity vectors and
fixed-at-compile-time arrays. So you might create:
std2::vector<int> a(3); // not three items, but capacity for three items
a.push_back(1); // returns result
2017-06-23 3:13 GMT+02:00 Niall Douglas via Boost
However I am thinking here in terms of WG21 standardisation, specifically SG14's work on a std::vector upgrade which doesn't have the really unfortunate unpredictable latency. The general idea is that a low latency std::vector would never expand its capacity automatically, instead it would return success + capacity approaching warning status. You then could schedule the construction of a new, bigger vector outside the hot path.
I find this example as unconvincing as Lawrence's original one. For one, vector functions that grow the size typically return void. So you could just turn that into bool or an enum and you'd be done.
For another, when you're in the hot path, the "capacity warning" status is not actionable, so it's of little use. Instead, when you get out of the hot path (or, better yet, before you get into it), you'd simply check capacity(), see if it's yellow/red, and reserve. Or not even check, just reserve( size() + <hot path max size requirement> ).
All valid observations.
Yet SG14 likes anything which relocates latency unpredictable things (exception throws, malloc, etc) to the caller. It's certainly how I'd have personally designed the STL originally. std::vector's latency insensitive design has always irritated me, and I still hope - without much basis - that any STL v2 will fix that.
A superior alternative to vector? -- ok, but this is not clear how the "value + status" vocabulary type helps here. It is not enough to say, I can return two things. You would also have to demonstrate, how users will work with such status value: if it is even possible, without compromising the clarity of the code.
I'm minded to try and deliver SG14 a vocabulary type which can help with that so they can propose it for standardisation. As much as it'll cause problems with the Boost review, it delights SG14 members. I received quite a lot of private email during and after the Outcome v1 review about how from the review feedback on boost-dev it was clear here doesn't understand SG14's needs and priorities. I really wish they'd say that here instead of emailing me privately about it. I already know, it's a difference of mindset and philosophy.
So, let's summarize what you are saying here: 1. Your goal is to service "SG14 people". 2. "SG14 people" do not trust Boost community could understand/appreciate their needs to the extent that they do not even offer their input during the review. 3. You want this library for "SG14 people" to go through Boost review, and become part of Boost. Maybe this is a wrong way to go, because what you will hear here is, "not well justified", "no demonstrated use cases that would be improved by your library". Alternatively, what you could do, given the fact that "SG14 people" refuse to give their input in boost-dev, is to act like a proxy and convey their arguments here. But so far, you have been only conveying the "I need this" part, not the really interesting part: "when I have it I can do this". In the other message you showed us how a vector with capacity fixed upon construction is better than `std::vector`, but it didn't illustrate why there is a need for a "value + status" type. I realize my expectations must be frustrating. On the other hand, without it, I do not see how people in boost-dev can fulfill their task of giving you a valuable feedback. Regards, &rzej;
On 23 June 2017 at 03:25, Peter Dimov via Boost
For one, vector functions that grow the size typically return void. So you could just turn that into bool or an enum and you'd be done.
Maybe pop_back() could be made to return the popped element instead of void. degski -- "*Ihre sogenannte Religion wirkt bloß wie ein Opiat reizend, betäubend, Schmerzen aus Schwäche stillend.*" - Novalis 1798
On Thu, Jun 22, 2017 at 9:18 PM, degski via Boost
On 23 June 2017 at 03:25, Peter Dimov via Boost
wrote: For one, vector functions that grow the size typically return void. So you could just turn that into bool or an enum and you'd be done.
Maybe pop_back() could be made to return the popped element instead of void.
You'd be paying for a move you didn't want to buy.
degski wrote:
On 23 June 2017 at 03:25, Peter Dimov via Boost
wrote: For one, vector functions that grow the size typically return void. So you could just turn that into bool or an enum and you'd be done.
Maybe pop_back() could be made to return the popped element instead of void.
pop_back doesn't grow the size. It never needs to reallocate. As for why it doesn't return the element, because copying it may throw.
2017-06-23 0:24 GMT+02:00 Niall Douglas via Boost
So result
is either wearing a "status hat", or it is wearing a "failure hat". It cannot be both. The only remaining objection now is that result<...> is named the same yet has different semantics depending on its S type. Yet error_code is also a status, it's just a *negative* status rather than a *positive* status.
I've pushed clarifying reference API docs regarding this trait separation of positive versus negative status types to https://dedi4.nedprod.com/static/result-v2/doc_result.html.
I appreciate that this change is controversial. However, I am also minded that there is no point in Outcome v2 covering the identical ground as Expected, just less well. Outcome ought to be isomorphic to Expected, be strong at the areas Expected is weak at and so on.
It seems to me you do not appreciate the value of Outcome v1. The ground is not identical to Expected. You said you refused to implement some parts of Expected because of practical considerations -- I was convinced by that as this indicated that you have tested it in practice both as a user and as an implementer. The significant value of Outcome v1 is that you have used it already in two big libraries. It has been tested in the field. Hence, probably, the practical solutions like the `TRY` operation. With this library comes a big practical experience of the author, the micro optimizations, usage idioms. Now with Outcome v2, you seem to loose this advantage; you are implementing a theoretical feature, which have never been tested in practice, of which it is not clear what users expect, and if it even be used. Regards, &rzej;
2017-06-22 23:09 GMT+02:00 Niall Douglas via Boost
You could say "if you do not like the state `has both value and error`, then don't create it", and this could be considered acceptable, but now that you have said you intend to change "error" into "status", you are affecting even this conservative usage.
In the paper by Laurence Crowl, in the example with queue status:
``` Value queue::value_pop(); queue_op_status queue::wait_pop(Value&); ```
what he is describing is: pair
Whereas what you provide (if I got it correctly) is: pair
Which means I can get a value with no status. This still does not implement P0262.
In response to yours and Peter's concerns, I've now tightened the semantics considerably. For result
you now may do: 1. Construct with a T. .value() returns that T. .status() throws bad_result_access. There is no .error().
2. Construct with a T and an S. .value() returns that T, .status() returns that S. There is no .error().
And that's it. If trait::enable_errored_result_creation<S> is true however, you get this instead:
1. Construct with a T. .value() returns that T. .error() throws bad_result_access. There is no .status().
2. Construct with an S. .value() throws that S according to policy. .error() returns that S. There is no .status().
So result
is either wearing a "status hat", or it is wearing a "failure hat". It cannot be both. The only remaining objection now is that result<...> is named the same yet has different semantics depending on its S type. Yet error_code is also a status, it's just a *negative* status rather than a *positive* status.
If you are determined to go this way, why not offer two separate templates: `annotated` and `result`. The former for the "status" functionality, the latter for the Outcome-v1-like "either T or error", and have them just share the same implementation. Since they have different semantics and member functions why have them share the same name? Regards, &rzej;
The only remaining objection now is that result<...> is named the same yet has different semantics depending on its S type. Yet error_code is also a status, it's just a *negative* status rather than a *positive* status.
If you are determined to go this way, why not offer two separate templates: `annotated` and `result`. The former for the "status" functionality, the latter for the Outcome-v1-like "either T or error", and have them just share the same implementation. Since they have different semantics and member functions why have them share the same name?
So good news for you, yesterday after pondering things over the weekend
I disabled the positive status functionality. It can be reenabled via
macro, but it's off by default. result
On Wed, Jun 21, 2017 at 7:23 PM, Niall Douglas via Boost < boost@lists.boost.org> wrote:
On 22/06/2017 02:08, Peter Dimov via Boost wrote:
Niall Douglas wrote:
The idea, since we no longer have variant storage, is to see if we'd like to implement
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0262r0.html (except with the parameter order reversed).
I'm not particularly enthusiastic about status_value (and the argument given in the paper is unconvincing.) Neither am I thrilled by the idea of result
(an optional) or result (something like a glorified pair .) I've fixed the problems you raised at https://dedi4.nedprod.com/static/result-v2/doc_result.html and moved the implementation stuff into a impl namespace so standardese will still show the docs for the composited types, but not be usable by end users.
result
in its original form represented a very clear concept: the type of a function that must either return a value, or a reason for the lack of value. All of the above do not fit this concept and to me ring hollow. Andrzej feels similarly to you.
But I don't think things are as bad as both of you feel. In v2 result
you now can have: 1. Successful: T
2. Failure: EC
3. Successful + Status: T + EC
The reason I don't think this is a problem is because you can't accidentally return a wrong result
in practice. Either you're using result to return Success + Status
If I wanted to return Sucess + Status, I would return pair
2017-06-21 19:41 GMT+02:00 Niall Douglas via Boost
Dear Boost,
I've been hard at work refactoring Outcome to match the peer review a few weeks ago. Much comment at the time mentioned the poor experience of the reference API docs, so can I get a community check of this:
https://dedi4.nedprod.com/static/result-v2/doc_result.html
This was generated by the new C++ reference docs generator https://github.com/foonathan/standardese. You'll no doubt spot quite a few formatting bugs, Jonathan says he's onto them. But the usage of libclang rather than doxygen's broken C++ parser clearly shows a lot of benefit, it's just standardese still remains quite a lot short in terms of necessary features to make output fully correct.
I won't give the changelog for Outcome v2 result<T> over Outcome v1 yet. Let's see how you fare with the above reference API docs first. But as you'll note, there is considerable change over before as we now slot into a niche not occupied by Expected nor EitherMonad, indeed this now looks much more like Robert's or Lawrence Crowl's slimmed down object. This result<T> is approx 0.6 CPU cycles per stack frame unwound slower which I feel is acceptable in exchange for no more variant storage, which in turn released lots of SFINAE budget for me.
(FYI v2 outcome<T> isn't done yet, but it will extend v2 result<T>)
Niall, There is certainly more information now in the reference section than before. This is clearly an improvement. You probably want to conceal some of the types used for implementation from the docs, like those from namespace `impl`, but I understand this is just a prototype. One thing I want to suggest, is that the standardese used in the standard Library has problems of its own, that you have a chance of avoiding. For instance, as described here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0411r0.html the "Requires" clause mixes two things: (1) requirements that can be (and are) checked at compile-time (like DefaultConstructible), and (2) requirements on values that if not met cause UB (like `i < this->size()`). The linked paper proposes, and I encourage the same to use two different clauses: Precondition: i < this->size(); Requires: is DefaultConstructible And as some have suggested, even a third one that describes all the enable_if-s in the interface: "Enabled_if: is_copyable_v<T>" instead of "Effects: this overload shall not participate in overload resolution unless is_copyable_v<T> is true". Regards, &rzej;
There is certainly more information now in the reference section than before. This is clearly an improvement. You probably want to conceal some of the types used for implementation from the docs, like those from namespace `impl`, but I understand this is just a prototype.
Namespace impl is purely due to current limitations in the standardese tool. It's supposed to be namespace detail.
One thing I want to suggest, is that the standardese used in the standard Library has problems of its own, that you have a chance of avoiding. For instance, as described here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0411r0.html
the "Requires" clause mixes two things: (1) requirements that can be (and are) checked at compile-time (like DefaultConstructible), and (2) requirements on values that if not met cause UB (like `i < this->size()`). The linked paper proposes, and I encourage the same to use two different clauses:
Precondition: i < this->size(); Requires: is DefaultConstructible
And as some have suggested, even a third one that describes all the enable_if-s in the interface:
"Enabled_if: is_copyable_v<T>" instead of "Effects: this overload shall not participate in overload resolution unless is_copyable_v<T> is true".
Enhancement submitted to https://github.com/foonathan/standardese/issues/62 Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
participants (5)
-
Andrzej Krzemienski
-
degski
-
Emil Dotchevski
-
Niall Douglas
-
Peter Dimov