On 29/05/2017 08:23, Gavin Lambert via Boost wrote:
On 28/05/2017 10:46, Niall Douglas wrote:
I would agree. But well, we were outvoted. And that probably means rejection of this library, as the presented library does not implement what the majority want (yet).
Just to clarify the meaning, since I'm not especially fluent in standardese: by "narrow contract" you mean "has UB if you don't include external checks", correct? I really don't like that even being an option in a type intended to improve error handling.
If you feel this way too, I think between me, you and Peter we now are equal in number with the other group. Of course it's not about numbers of votes, but about feeling out where the majority opinion is at.
I personally think I'll have very little use for the narrow contract varieties, but I've been convinced by the non-empty-capable vs empty-capable distinction because it means that my public API functions can specify via the type they return whether an empty return is possible or not, thus more accurately specifying their public contract. That, and the fact I can still use a receiving empty-capable variety for detecting when loops haven't found what they were looking for etc I find very compelling.
Doesn't that make things more complicated, though? Library A uses one flavour of it and library B uses a different flavour; how do they interoperate? (I know both can coexist due to namespacing and other things, but still if one method calls the other there has to be some kind of handover of the return value.)
Outcome implements well defined directional implicit conversion between flavours. It does now, and would continue to do so.
For that matter, what happens when a method in library A (using the guaranteed-never-empty flavour) calls a method in library B (using the empty-by-default flavour)? Can they put the return value into one of their never-empty outcomes (but then what if it's empty?) or do they have to work with the might-be-empty outcome type that's foreign to their own code and way they want to do things, and thus more likely to be used incorrectly?
Under what I said I would do in the high level summary, the programmer would need to manually unpack the empty-capable object, deal with the empty state if present, and construct an non-empty-capable object for return. No implicit nor explicit conversion exists between empty-capable and non-empty-capable.
Typedefs can sort out the differences to a certain extent, but then you need to remember to use the right one with each library (either getting unexpected behaviour or the compiler yelling at you when incorrect, depending on implementation, neither of which is particularly desirable), and there's possible interoperability issues for storing the results from multiple libraries into a single vector, for example.
I believe I have been persuaded to drop the checked_* separate types in favour of unchecked, but longer named, new member functions. I'll be posting a v2 high level summary later today. I don't think accumulating disparate typed results into a vector is a problem - just use the most representative type possible. Outcome deliberately implements a hierarchy of types with a most representative edition at the bottom. It can always accumulate anything from any less representative sibling type. I use this behaviour extensively in my own code.
What about interop between a v1 outcome and a v2 outcome? That's no longer an identical implementation.
My current intention is that the programmer would need to unpack and pack at such a boundary. Safest, until we know more. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/