On Sun, Jul 16, 2017 at 7:00 PM, Gavin Lambert via Boost < boost@lists.boost.org> wrote:
On 14/07/2017 06:09, Emil Dotchevski wrote:
If std::error_code is sufficient, put your money where your mouth is and go back to outcome v1 where the only choices were std::error_code and std::exception_ptr. But the reality is that std::error_code is not sufficient (perhaps not due to its own deficiencies); in practice you do need to be able to transport more or less arbitrary error types.
I think the issue is that while std::error_code is sufficient to convey an error *code*, it is not sufficient to convey an error *context*.
You're saying that std::error_code is fully capable of conveying _what_ went wrong. This is true, but is it sufficient for an error handling library to only support std::error_code for this purpose? The answer in v1 was "almost", as it also supported std::exception_ptr. The answer in v2 seems to be "not at all", since it turned both into template parameters. The context part is a separate issue and you're right that it can be provided independently of the error type.
Outcome v1 solved this by standardising on a slightly-larger type that conveyed a specific small set of additional data, which was deemed good enough for most uses -- but there's still a bit of sacrifice involved there since it would be too much for some cases and too little for others, combined with the separated storage causing it to sometimes go away unexpectedly.
Yes, it was a compromise, which is the result of the decision to transport error objects into the return value of functions. This design -- and the compromises that follow from it -- would be more reasonable elsewhere, but in error handling very few functions need to interact with the error object. That is why it makes more sense to push it out of the critical path. I mentioned in a discussion thread at the time that it might be useful to
consider having multiple user-defined derived subtypes of std::error_code (or one standard templated one) to add additional context state, similar to how std::exception is subclassed to add additional state to that. Though this has some downsides as well, mainly requirement to not pass by value (unless you want to slice off the extra data) and possible introduction of memory allocations (eg. if std::string were in the payload), which also discourages pass-by-value.
If you enable subclassing, you're enabling more or less arbitrary types. At that point there is little value in mandating std::error_code as the only acceptable base type, in fact it makes more sense to use std::exception. You're right about memory allocations, but while they are not permissible in some cases in others they can be used, and should be supported.
Outcome v2 appears to have chosen a different path, where you mostly still use unadorned std::error_code (although since it's templated it's possible you could still do the above) but you also get an extra arbitrary payload pointer *or* an exception_ptr (where then presumably your extra payload is carried in a particular exception subclass). I'm still a bit on the fence about that latter option; exception_ptrs are a bit of a pain to extract useful information from.
It is true that std::exception_ptr probably needs facilities to inspect the object it contains.