On 28.1.2016. 7:49, Michael Marcin wrote:
So looking a few of these libraries/proposals I have a few things I'm interested in.
- Is it at least as good as a simple hand-rolled solution?
As Niall said, assuming the lib dev paid attention to codegen (i.e. testing with the disassembly window always open) it should be _better_ then a hand-rolled (i.e. reinvented wheel) solution as today's compilers are often very smart but sometimes also face-palmingly-dumb and it takes effort to make'em happy (if possible at all)...
- Can you transport error codes through layers without knowing specifics of an error?
For the second Niall's outcome/result handles very well I think. std::error_code and std::exception_ptr transport is good enough for almost any reasonable type agnostic error transport.
There are two classical approaches to type agnosticism: (a) compile-time/templates and (b) runtime/type-erasure (codes, exception_ptrs, virtual functions/polymorphism). Since you can at any point go from (a) to (b) but not vice-verse, and since (a) is easier on the compiler/optimizer (to ensure that you don't pay for what you don't use), I'd say that the basic solution (the basic, underlying class template, or two of them as in the case of Err) should go with (a). Why should code in a particular 'layer'/confined to an ABI and/or API border, let's call it Lib1, tug around a generic std::error_code (with a pointer to Lib1ErrorCategory) instead of just using some lib1::error object which can be (e.g. implicitly) converted to std::error_code or to std::exception_ptr or to an exception object and throw at the API boundary? Sure the library can and should help with automating such (a)->(b) conversions (like std provides functions and traits that can be specialized for UDTs to automate conversion between error enum values, error_codes and error_conditions, or like Err provides a function for transforming error objects into exception objects, etc.) but those are 'implementation details'...
If the direct caller can handle the error returning an error enum instead of a std::error_code will often be better. It looks like the DXXXXR0 expected proposal allows for this.
A variant is a good implementation for many types. And I think clearly the right default behavior. However, I'm not convinced that a variant is always the best implementation.
Well AFAICT all the implementations are/have to be based on discriminated unions/'variants' of some kind...WRT to QoI you just have to stop thinking about it as just another boost::variant (for example it is limited to exactly two types which immediately opens up space for 'clever specialisations')...
Take for example an expected
. Although I haven't done tests I'd be reasonable confident returning this by value would perform worse than returning std::pair . First, the machinery is going to make it store an extra byte at least for the variant discriminator.
Then, you likely branch before the copy (maybe optimized away by a smart variant implementation).
In fact in *most* cases of returning a pointer and error_enum you should be able to get away with returning a the equivalent of union { void*, error_enum } As *most* error_enums are relatively small and have values smaller than any valid heap or stack pointer. It would be nice to be able to opt in for this.
As you yourself hint here, most or all of this overhead can be 'optimised' (so as to perform _at least_ as good as hand written solution while still using the intuitive generic interface) under the condition that we don't fix/limit it to use predefined 'runtimey' error abstractions like std::error_code (which are larger than the CPU word and touch things with vtables which in turn make them harder to pass/return in registers and cause other bloat like RTTI records)... For example err::result_or_error has a specialization for 'empty' error objects (use for APIs that use thread local 'last error' codes, like Win32 or errno)... But, again, these are 'implementation details' and I don't think we've gone far enough in the 'bikeshedding' to deal with that ;) (IMO the important thing is just the agreement that the chosen solution must allow for optimal codegen with existing or near-future compilers...)
More generally given - an error_enum with an OK or 'not an error' value - a T that is cheap to default construct and cheap to copy/move Is the library solution better than returning a pair
? Or a function returning an error_enum with an out param?
Yes it is better (a library solution), because it can be made to produce 'just as good' codegen (or in some cases it is theoretically possible for some compilers to finally get their act together produce decent codegen) while providing a reusable solution... -- "What Huxley teaches is that in the age of advanced technology, spiritual devastation is more likely to come from an enemy with a smiling face than from one whose countenance exudes suspicion and hate." Neil Postman