Boost logo

Boost :

From: Emil Dotchevski (emildotchevski_at_[hidden])
Date: 2020-05-29 19:22:51


On Fri, May 29, 2020 at 1:26 AM Alexander Grund via Boost <
boost_at_[hidden]> wrote:
> Maybe improve on `remote_handle_all` in 2 ways: Create the lambda taking
> `leaf::error_info const&` in that function to remove the boilerplate at
> the user call site. Given that rename it to e.g. `create_remote_handler`
> or so. I find the current interface confusing and the documentation
> calls it "unintuitive", so I guess this should be improved on before
> release. Given that: It seems `remote_try_handle_all` can be folded into
> the regular `try_handle_all`. So `try_handle_all` takes a list of
> handlers where one could be the aggregated handler defined by
> `create_remote_handler` (then maybe `create_aggregate_handler`?)

I've explored folding it all into try_handle_all, but the implementation
gets really messy. Perhaps it can be done. Consider that in this case the
loop that matches the lambdas based on what error objects are available at
runtime now has to be recursive. Maybe it can be done elegantly, but I
tried and gave up.

> I'm interested in knowing how `try_handle_all` implements "if the user
> fails to supply at least one handler that will match any error, the
> result is a compile error". If arbitrary functions can be called
> throwing any error type, how could that work? Is it again required to
> use a catch-all clause?

Yes, you must supply a handler that matches any error. If you don't want to
do that, use try_handle_some. The benefit of _all is that it unwraps the
result for you: where try_handle_some returns a result<T>, try_handle_all
returns T. It also guarantees at compile time that the user has provided a
suitable handler for all errors.

> > - What is your evaluation of the documentation?
> Good overall, but the details are to detailed for an introduction and to
> coarse for understanding it.

Thanks for this feedback.

> - "Using Exception Handling": virtual inheritance is mentioned but the
> reasoning is not clear to me. I have never used or seen it used in that
> context and always strive to avoid it due to the issues with handling
> such an object (starting at writing ctors)

This is a somewhat-known problem with exceptions. If you have:

struct A { };
struct B1: A { };
struct B2: A { };
struct C: B1, B2 { }

Now a catch(A &) won't intercept a C because the cast is ambiguous. Using
virtual inheritance eliminates this problem and allows A to be safely used
as a base of anything that should be classified as A.

> - "Accumulation": shows how to call leaf::accumulate but I don't see
> what it use is. "Many different files" are mentioned but the operation
> only takes a single file. And based on everything before a single error
> stops execution (like exceptions)

Normally you give LEAF an object of type T and it stores it away for you.
If you later give it another value of type T, it'll just overwrite the old
one. With accumulate, you get a mutable reference to the stored value (and
it requires T to have a default constructor).

> - "error_id": It is unclear to me what it's use is. Especially as
> something unique in the program in the context of threads means some
> kind of synchronization which will be paid for even if unused.

See https://zajo.github.io/leaf/#tutorial-model

> - "defer": I'd like to know when the lambda is not called. The
> documentation says it is called in the dtor, but later: "function passed
> to defer, if invoked,"

The logic is the same as for preload, which takes your value, and will
later communicate it if an error has occurred, while defer takes your
lambda and will later call it to get a value to communicate, if an error
has occurred.

> I'd also like to see a comparison with exceptions especially in cases
> where the usage of result<T> inhibits (N)RVO (even more for C++17 and
> up). This is my main critic point about result<T> type of error handling
> as that variant type creates less efficient code for the happy path.

Yes, though leaf::result<T>, in case of an error, only transports a single
int, the error_id, which allows for efficient implementation.

That said, you can use pretty much any result type with LEAF, it does not
have to be leaf::result<T>.

Thanks!


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