Boost logo

Boost :

Subject: Re: [boost] expected/result/etc
From: Domagoj Saric (dsaritz_at_[hidden])
Date: 2016-01-28 18:26:17


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<void*,error_enum>.
> Although I haven't done tests I'd be reasonable confident returning this
> by value would perform worse than returning
> std::pair<void*,error_enum>.
>
> 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<T, error_enum>?
> 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

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk