LEAF has been refactored, it now has a much simplified error handling interface
LEAF is a C++11 error handling library for use in low-latency environments. I'm looking for a Boost Review Manager. Any one of you good people willing to volunteer? :) https://zajo.github.io/leaf/ Features: -Header-only, no dependencies. - No dynamic memory allocations. - Any error-related object of any movable type is efficiently delivered to the correct error handler. - Compatible with std::error_code, errno and any other error code type. - Support for multi-thread programming. - Can be used with or without exception handling.
On Wed, Jan 16, 2019 at 11:51 AM Emil Dotchevski via Boost
LEAF is a C++11 error handling library for use in low-latency environments.
- Can be used with or without exception handling.
Is it possible to have a common error handling logic for both error codes and exceptions? Something like: leaf::result<T> function_that_may_throw(); ... leaf::handle_all( [i]() -> leaf::result<int> { LEAF_AUTO(r, leaf::exception_to_resultstd::exception([] { function_that_may_throw(); })); ... return 0; }, [](std::exception const &e) { // Handle exception(s) return 1; }, [](custom_error_code ec) { // Handle error code(s) return 2; }) The attached program shows that once leaf::exception_to_result() is used, only the exceptions reach the handlers in `handle_all`. Also, is there a way to access the "what" message of the exceptions in the error handlers, instead of just "std::exception"? Thanks, Sorin
On Mon, Jan 21, 2019 at 6:14 PM Sorin Fetche via Boost < boost@lists.boost.org> wrote:
On Wed, Jan 16, 2019 at 11:51 AM Emil Dotchevski via Boost
wrote: LEAF is a C++11 error handling library for use in low-latency
environments.
- Can be used with or without exception handling.
Is it possible to have a common error handling logic for both error codes and exceptions? Something like:
leaf::result<T> function_that_may_throw(); ... leaf::handle_all( [i]() -> leaf::result<int> { LEAF_AUTO(r, leaf::exception_to_resultstd::exception([] { function_that_may_throw(); })); ... return 0; }, [](std::exception const &e) { // Handle exception(s) return 1; }, [](custom_error_code ec) { // Handle error code(s) return 2; })
The attached program shows that once leaf::exception_to_result() is used, only the exceptions reach the handlers in `handle_all`.
You found a bug, thank you. I had not tested exception_to_result with a
function that returns result<T>, and it incorrectly deduced its return type
as result
Using try_ will now handle result<T> errors as well, see https://github.com/zajo/leaf/blob/feature/error_code/test/try_exception_and_... . With this, in your example instead of handle_all you can just use try_: leaf::try_( [i]() -> leaf::result<int> { LEAF_AUTO(r, f2(i)); (void) r; return 0; }, //handlers... ); The only difference is that in case f2 does not throw, try_ is not equivalent to handle_all but to handle_some, so the result<int> that will pop out of try_ should not be discarded (in case it is communicating an unmatched error). By the way, in this new iteration handle_some, handle_all and try_ don't require leaf::result<T>, they can work with any "result" type which defines .value() and .error(), as long as .error() returns std::error_code or leaf::error.
On Tue, Jan 22, 2019 at 2:34 PM Emil Dotchevski via Boost
Using try_ will now handle result<T> errors as well, see https://github.com/zajo/leaf/blob/feature/error_code/test/try_exception_and_... .
With this, in your example instead of handle_all you can just use try_:
leaf::try_( [i]() -> leaf::result<int> { LEAF_AUTO(r, f2(i)); (void) r; return 0; }, //handlers...
Yes, the output of the attached program shows closer to what I had in mind, if compiled with g++ 7 or 8, release mode. It has two problems though: - in release mode the program crashes if I try to print 'verbose_diagnostic_info' <snip> f2(7) Segmentation fault (core dumped) - in debug mode it crashes right at the beginning f2(0) leaf_test: /mnt/d/Sorin/Work/C++/cpp-playground/tmp/include/boost/leaf/detail/teleport.hpp:195: const std::exception* boost::leaf::error_info::exception() const: Assertion `has_exception()' failed. Aborted (core dumped) Thanks, Sorin
Thank you for testing. I fixed the first assert, I hadn't tested catch_<> when there is no exception. It works now. The diagnostic_info and verbose_diagnostic_info asserts are because those types were designed to detect attempts to propagate types which no handler cares about. In your example this doesn't happen, so LEAF doesn't instantiate diagnostic_info/verbose_diagonstic_info, but it thinks they must have been instantiated. :) I'll have to think a bit about this one. I'll probably make these types wrappers with a pointer inside, which then can be null in this case. In the mean time, the workaround is to make the handler take these types by const *, e.g.: [ ]( leaf::verbose_diagnostic_info const * v ) { if(v) std::cerr << *v; }
On Wed, Jan 23, 2019, 2:07 AM Emil Dotchevski via Boost < boost@lists.boost.org wrote:
Thank you for testing.
I fixed the first assert, I hadn't tested catch_<> when there is no exception. It works now.
I'm trying to evaluate how the LEAF library would work for transporting additional information besides error codes when implementing Asio asynchronous operations. So, my next test is to capture both errors and exceptions together with additional info and pass them to the completion handler that will do a detailed error analysis. I have two unsuccessful attempts at this on wandbox (together with leaf/all.hpp merged in a single file by a little Python script): - with try_: https://wandbox.org/permlink/t0r0IUntnga6zUUK - with capture_result: https://wandbox.org/permlink/OCfmA9WXEgSySE1E Any suggestions on how to change the test(s) to achieve this? Would this be a use case supported (efficiently, when no error occurred) by the LEAF library? Thanks, Sorin
I'm trying to evaluate how the LEAF library would work for transporting additional information besides error codes when implementing Asio asynchronous operations.
So, my next test is to capture both errors and exceptions together with additional info and pass them to the completion handler that will do a detailed error analysis.
The canonical way to implement this is to customise ASIO's async_result to pass additional info. See https://www.boost.org/doc/libs/1_69_0/doc/html/boost_asio/reference/async_re.... As it would be useful for Outcome to document this, I've logged an issue to https://github.com/ned14/outcome/issues/165 to explain how to customise ASIO's async_result for outcome::result<T>. Niall
On Wed, Jan 23, 2019 at 11:47 AM Sorin Fetche via Boost < boost@lists.boost.org> wrote:
On Wed, Jan 23, 2019, 2:07 AM Emil Dotchevski via Boost < boost@lists.boost.org wrote:
Thank you for testing.
I fixed the first assert, I hadn't tested catch_<> when there is no exception. It works now.
I'm trying to evaluate how the LEAF library would work for transporting additional information besides error codes when implementing Asio asynchronous operations.
So, my next test is to capture both errors and exceptions together with additional info and pass them to the completion handler that will do a detailed error analysis.
I have two unsuccessful attempts at this on wandbox (together with leaf/all.hpp merged in a single file by a little Python script):
- with try_: https://wandbox.org/permlink/t0r0IUntnga6zUUK
- with capture_result: https://wandbox.org/permlink/OCfmA9WXEgSySE1E
Any suggestions on how to change the test(s) to achieve this? Would this be a use case supported (efficiently, when no error occurred)
by
the LEAF library?
Yes, I should put this in FAQ in the documentation because it is subtle yet critical. This is correct: leaf::try_( [ ]() -> leaf::result<T> { return do_work(); //but usually the do_work body goes here, rather than a single function call. }, ..... //handlers ); This is incorrect: leaf::result<T> r = do_work(); leaf::try_( [r]() -> leaf::result<T> { return r; }, ..... //handlers ); The problem is that the memory where any errors reported to LEAF by do_work will be moved to is in the scope of leaf::try_, so anything that happens before you enter leaf::try_ won't be captured by *this* leaf::try_, but by some *other* leaf::try_, leaf::handle_some, or leaf::handle_all scope up the call chain, if any. You can _almost_ forget that leaf::result<T> doesn't contain any actual error objects... but not quite. :) If you haven't already, you can look at example/capture_eh.cpp and example/capture_result.cpp, they demonstrate the canonical way to transport error objects across threads. Let me know if I can help more.
śr., 16 sty 2019 o 20:51 Emil Dotchevski via Boost
LEAF is a C++11 error handling library for use in low-latency environments.
I'm looking for a Boost Review Manager. Any one of you good people willing to volunteer? :)
Features:
-Header-only, no dependencies.
- No dynamic memory allocations.
- Any error-related object of any movable type is efficiently delivered to the correct error handler.
- Compatible with std::error_code, errno and any other error code type.
- Support for multi-thread programming.
- Can be used with or without exception handling.
Hi Emil, In the docs we can see that one of the requirements for result<T> is that "T must be movable, and its move constructor may not throw." This seems to preclude guard-like types that are not movable to be returned from functions via result<T>. Like std::mutex. When a wrapper type is returned by value there is no need to move the internally stored T. At east in C++17. In principle, a wrapper should not require the wrapped type to be movable. Types like boost::optional or outcome::result do not have this requirement. They provide an "in-place" constructor for this purpose. Is it possible to lift the requirement in leaf::result? Also, the part "move constructor may not throw" is a bit ambiguous. It could mean: 1. The program will not compile unless T's move constructor is annotated as `noexcept` or `throw()`. 2. If a move constructor of T is invoked and it ends by throwing an exception the behavior of the program is undefined. IOW, can I pass types potentially throwing from move constructors (like std::list<int>) at my own risk or is it statically prevented? Regards, &rzej;
On Tue, Jan 29, 2019 at 1:36 AM Andrzej Krzemienski via Boost < boost@lists.boost.org> wrote:
śr., 16 sty 2019 o 20:51 Emil Dotchevski via Boost
napisał(a): LEAF is a C++11 error handling library for use in low-latency
environments.
I'm looking for a Boost Review Manager. Any one of you good people
willing
to volunteer? :)
Features:
-Header-only, no dependencies.
- No dynamic memory allocations.
- Any error-related object of any movable type is efficiently delivered to the correct error handler.
- Compatible with std::error_code, errno and any other error code type.
- Support for multi-thread programming.
- Can be used with or without exception handling.
Hi Emil, In the docs we can see that one of the requirements for result<T> is that "T must be movable, and its move constructor may not throw."
This seems to preclude guard-like types that are not movable to be returned from functions via result<T>. Like std::mutex. When a wrapper type is returned by value there is no need to move the internally stored T. At east in C++17. In principle, a wrapper should not require the wrapped type to be movable. Types like boost::optional or outcome::result do not have this requirement. They provide an "in-place" constructor for this purpose. Is it possible to lift the requirement in leaf::result?
Also, the part "move constructor may not throw" is a bit ambiguous. It could mean: 1. The program will not compile unless T's move constructor is annotated as `noexcept` or `throw()`. 2. If a move constructor of T is invoked and it ends by throwing an exception the behavior of the program is undefined.
IOW, can I pass types potentially throwing from move constructors (like std::list<int>) at my own risk or is it statically prevented?
Regards, &rzej;
Thank you for looking into this. You're right, the documentation is
imprecise. The answer is that leaf::result<T> is a variant
participants (4)
-
Andrzej Krzemienski
-
Emil Dotchevski
-
Niall Douglas
-
Sorin Fetche