diff --git a/doc/leaf.adoc b/doc/leaf.adoc index a537121..5331a04 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -1113,7 +1113,7 @@ The following predicates are available: * <>: as described above. * <>: where `match` compares the object `e` of type `E` with the values `V...`, `match_value` compare `e.value` with the values `V...`. * <>: similar to `match_value`, but takes a pointer to the data member to compare; that is, `match_member<&E::value, V...>` is equvialent to `match_value`. Note, however, that `match_member` requires {CPP}17 or newer, while `match_value` does not. -* `<>`: Similar to `match`, but checks whether the caught `std::exception` object can be `dynamic_cast` to any of the `Ex` types. +* `<>`: Similar to `match`, but checks whether the caught `std::exception` object can be `dynamic_cast` to any of the `Ex` types. * <> is a special predicate that takes any other predicate `Pred` and requires that an error object of type `E` is available and that `Pred` evaluates to `false`. For example, `if_not>` requires that an object `e` of type `E` is available, and that it does not compare equal to any of the specified `V...`. The predicate system is easily extensible, see <>. @@ -1242,7 +1242,7 @@ Error handling functions accept a `std::tuple` of error handlers in place of any Like exceptions, LEAF error objects are local to a thread. When using concurrency, sometimes we need to collect error objects in one thread, then use them to handle errors in another thread. -LEAF supports this functionality with or without exception handling. In both cases error objects are captured and transported in a `leaf::<>` object. +LEAF supports this functionality with or without exception handling. In both cases error objects are captured and transported in a `leaf::<>` object. [[tutorial-async_result]] ==== Transporting Errors Between Threads Without Exception Handling @@ -1254,7 +1254,7 @@ Let's assume we have a `task` that we want to launch asynchronously, which produ leaf::result task(); ---- -Because the task will run asynchronously, in case of a failure we need to capture any produced error objects but not handle errors. We do this by invoking `try_handle_some`, passing an error handler that takes an argument of type <>: +Because the task will run asynchronously, in case of a failure we need to capture any produced error objects but not handle errors. We do this by invoking `try_capture_all`: [source,c++] ---- @@ -1264,23 +1264,15 @@ std::future> launch_task() noexcept std::launch::async, [&] { - return leaf::try_handle_some( - [&]() -> leaf::result - { - return task(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; - }); + return leaf::try_capture_all(task); } ); } ---- [.text-right] -<> | <> +<> | <> -The `dynamic_capture` object passed to the error handler can hold multiple error objects of different types, at the cost of dynamic memory allocations. Returning it from the error handler converts it to `leaf::result`, which now holds the captured error objects. The `result` object can then be stashed away or moved to another thread, and later passed to an error-handling function to unload its content and handle errors: +In case of a failure, the returned from `try_capture_all` `result` object holds all error objects communicated out of the `task`, at the cost of dynamic allocations. The `result` object can then be stashed away or moved to another thread, and later passed to an error-handling function to unload its content and handle errors: [source,c++] ---- @@ -1314,7 +1306,7 @@ return leaf::try_handle_some( [.text-right] <> | <> | <> -NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/example/dynamic_capture_result.cpp?ts=4[dynamic_capture_result.cpp]. +NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/example/try_capture_all_result.cpp?ts=4[try_capture_all_result.cpp]. [[tutorial-async_eh]] ==== Transporting Errors Between Threads With Exception Handling @@ -1326,7 +1318,7 @@ Let's assume we have an asynchronous `task` which produces a `task_result` but c task_result task(); ---- -We use `try_catch` together with `dynamic_capture` to capture all error objects and the `std::current_exception()` in a `result`: +We use `try_capture_all` to capture all error objects and the `std::current_exception()` in a `result`: [source,c++] ---- @@ -1336,21 +1328,13 @@ std::future launch_task() std::launch::async, [&] { - return leaf::try_catch( - [&]() -> leaf::result - { - return task(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; - }); + return leaf::try_capture_all(task); } ); } ---- [.text-right] -<> +<> To handle errors after waiting on the future, we use `try_catch` as usual: @@ -1384,7 +1368,7 @@ return leaf::try_catch( [.text-right] <> | <> -NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/example/dynamic_capture_eh.cpp?ts=4[dynamic_capture_eh.cpp]. +NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/example/try_capture_all_eh.cpp?ts=4[try_capture_all_eh.cpp]. ''' @@ -1589,7 +1573,7 @@ leaf::result compute_answer() noexcept [.text-right] <> | <> -The `exception_to_result` template takes any number of exception types. All exception types thrown by the passed function are caught, and an attempt is made to convert the exception object to each of the specified types. Each successfully-converted slice of the caught exception object, as well as the return value of `std::current_exception`, are copied and <>, and in the end the exception is converted to a `<>` object. +The `exception_to_result` template takes any number of exception types. All exception types thrown by the passed function are caught, and an attempt is made to convert the exception object to each of the specified types. Each successfully-converted slice of the caught exception object, as well as the return value of `std::current_exception`, are copied and <>, and in the end the exception is converted to a `<>` object. (In our example, `error_a` and `error_b` slices as communicated as error objects, but `error_c` exceptions will still be captured by `std::exception_ptr`). @@ -1719,7 +1703,7 @@ int do_work( lua_State * L ) noexcept <3> Generate a new `error_id` and associate a `do_work_error_code` with it. Normally, we'd return this in a `leaf::result`, but the `do_work` function signature (required by Lua) does not permit this. <4> Tell the Lua interpreter to abort the Lua program. -Now we'll write the function that calls the Lua interpreter to execute the Lua function `call_do_work`, which in turn calls `do_work`. We'll return `<>`, so that our caller can get the answer in case of success, or an error: +Now we'll write the function that calls the Lua interpreter to execute the Lua function `call_do_work`, which in turn calls `do_work`. We'll return `<>`, so that our caller can get the answer in case of success, or an error: [source,c++] ---- @@ -2636,24 +2620,14 @@ namespace boost { namespace leaf { typename std::decay()())>::type try_catch( TryBlock && try_block, H && ... h ); - ////////////////////////////////////////// - #if BOOST_LEAF_CFG_CAPTURE - class dynamic_capture - { - //No public constructors - - bool empty() const noexcept; - int size() const noexcept; - - template - operator result() const noexcept; - - template - friend std::ostream & operator<<( std::basic_ostream &, dynamic_capture const & ); - }; + template + result // T deduced depending on TryBlock return type + try_capture_all( TryBlock && try_block ); #endif + ////////////////////////////////////////// + class error_info { //No public constructors @@ -2689,10 +2663,10 @@ namespace boost { namespace leaf { ---- [.text-right] -Reference: <> | <> | <> | <> | <> | <> | <> +Reference: <> | <> | <> | <> | <> | <> | <> ==== -[[handle_errors.hpp]] +[[to_variant.hpp]] ==== `to_variant.hpp` ==== @@ -2931,7 +2905,7 @@ namespace boost { namespace leaf { } } ---- -This function can be used to catch exceptions from a lower-level library and convert them to `<>`. +This function can be used to catch exceptions from a lower-level library and convert them to `<>`. Returns: :: Where `f` returns a type `T`, `exception_to_result` returns `leaf::result`. @@ -3139,15 +3113,15 @@ The `throw_exception` function is overloaded: it can be invoked with no argument <1> Selected if the first argument is not of type `error_id` and is an exception object, that is, iff `Ex` derives publicly from `std::exception`. In this case the thrown exception is of unspecified type which derives publicly from `Ex` *and* from class <>, such that: * its `Ex` subobject is initialized by `std::forward(ex)`; -* its `error_id` subobject is initialized by `<>(std::forward(e)...`). +* its `error_id` subobject is initialized by `<>(std::forward(e)...`). <2> Selected if the first argument is not of type `error_id` and is not an exception object. In this case the thrown exception is of unspecified type which derives publicly from `std::exception` *and* from class `error_id`, such that: ** its `std::exception` subobject is default-initialized; -** its `error_id` subobject is initialized by `<>(std::forward(e1), std::forward(e)...`). +** its `error_id` subobject is initialized by `<>(std::forward(e1), std::forward(e)...`). <3> If the fuction is invoked without arguments, the thrown exception is of unspecified type which derives publicly from `std::exception` *and* from class `error_id`, such that: ** its `std::exception` subobject is default-initialized; -** its `error_id` subobject is initialized by `<>()`. +** its `error_id` subobject is initialized by `<>()`. <4> Selected if the first argument is of type `error_id` and the second argument is an exception object, that is, iff `Ex` derives publicly from `std::exception`. In this case the thrown exception is of unspecified type which derives publicly from `Ex` *and* from class <>, such that: ** its `Ex` subobject is initialized by `std::forward(ex)`; @@ -3207,7 +3181,7 @@ Requires: :: * This function is only available under {CPP}-17 or newer. * The `try_block` function may not take any arguments. -* The type returned by the `try_block` function must be a `result` type (see <>). It is valid for the `try_block` to return `leaf::<>`, however this is not a requirement. +* The type returned by the `try_block` function must be a `result` type (see <>). It is valid for the `try_block` to return `leaf::<>`, however this is not a requirement. The `to_variant` function uses <> internally to invoke the `try_block` and capture the result in a `std::variant`. On success, the variant contains the `T` object from the produced `result`. Otherwise, the variant contains a `std::tuple` where each `std::optional` element contains an object of type `E~i~` from the user-supplied sequence `E...`, or is empty if the failure did not produce an error object of that type. @@ -3240,6 +3214,39 @@ assert(std::get<2>(t).value() == E3::e33); <3> ''' +[[try_capture_all]] +=== `try_capture_all` + +.#include +[source,c++] +---- +#if BOOST_LEAF_CFG_CAPTURE + +namespace boost { namespace leaf { + +template +result // T deduced depending on TryBlock return type +try_capture_all( TryBlock && try_block ) noexcept; + +} } + +#endif +---- + +Return type: :: An instance of `leaf::<>`, where T is deduced depending on the return type `R` of the `TryBlock`: +* If `R` is a some type `Result` for which <> is true, `try_capture_all` returns `leaf::<>`. +* Otherwise it is assumed that the `TryBlock` reports errors by throwing exceptions, and the return value of `try_capture_all` is deduced as `leaf::result`. + +Effects: :: `try_capture_all` executes `try_block`, catching and capturing all exceptions and all communicated error objects in the returned `leaf::result` object. The error objects are allocated dynamically. + +WARNING: Calls to `try_capture_all` must not be nested in `try_handle_all`/`try_handle_some`/`try_catch` or in another `try_capture_all`. + +NOTE: Under `BOOST_LEAF_CFG_CAPTURE=0`, `try_capture_all` is unavailable. + +See also: :: <>. + +''' + [[try_catch]] === `try_catch` @@ -3307,19 +3314,18 @@ namespace boost { namespace leaf { Requires: :: * The `try_block` function may not take any arguments. -* The type `R` returned by the `try_block` function must be a `result` type (see <>). It is valid for the `try_block` to return `leaf::<>`, however this is not a requirement. +* The type `R` returned by the `try_block` function must be a `result` type (see <>). It is valid for the `try_block` to return `leaf::<>`, however this is not a requirement. * Each of the `h...` functions: ** must return a type that can be used to initialize an object of the type `R`; in case R is a `result` (that is, in case of success it does not communicate a value), handlers that return `void` are permitted. If such a handler is selected, the `try_handle_some` return value is initialized by `{}`; ** may take any error objects, by value, by (`const`) reference, or as pointer (to `const`); -** may take arguments, by value, of any predicate type: <>, <>, <>, <>, <>, or of any user-defined predicate type `Pred` for which `<>::value` is `true`; -** may take a <> argument by `const &`; +** may take arguments, by value, of any predicate type: <>, <>, <>, <>, <>, or of any user-defined predicate type `Pred` for which `<>::value` is `true`; ** may take an <> argument by `const &`; ** may take a <> argument by `const &`; ** may take a <> argument by `const &`. Effects: :: -* Creates a local `<>` object `ctx`, where the `E...` types are automatically deduced from the types of arguments taken by each of `h...`, which guarantees that `ctx` is able to store all of the types required to handle errors. +* Creates a local `<>` object `ctx`, where the `E...` types are automatically deduced from the types of arguments taken by each of `h...`, which guarantees that `ctx` is able to store all of the types required to handle errors. * Invokes the `try_block`: ** if the returned object `r` indicates success [.underline]#and# the `try_block` did not throw, `r` is forwarded to the caller. ** otherwise, LEAF considers each of the `h...` handlers, in order, until it finds one that it can supply with arguments using the error objects currently stored in `ctx`, associated with `r.error()`. The first such handler is invoked and its return value is used to initialize the return value of `try_handle_some`, which can indicate success if the handler was able to handle the error, or failure if it was not. @@ -3399,38 +3405,13 @@ try_handle_some( <1> This handler can be selected to handle any error, because it takes `e_file_name` as a `const *` (and nothing else). <2> If an `e_file_name` is available with the current error, print it. + -* If `a~i~` is of a predicate type `Pred` (for which `<>::value` is `true`), `E` is deduced as `typename Pred::error_type`, and then: +* If `a~i~` is of a predicate type `Pred` (for which `<>::value` is `true`), `E` is deduced as `typename Pred::error_type`, and then: ** If `E` is not `void`, and an error object `e` of type `E`, associated with `err`, is not currently stored in `ctx`, the handler is dropped; otherwise the handler is dropped if the expression `Pred::evaluate(e)` returns `false`. ** if `E` is `void`, and a `std::exception` was not caught, the handler is dropped; otherwise the handler is dropped if the expression `Pred::evaluate(e)`, where `e` is of type `std::exception const &`, returns `false`. ** To invoke the handler, the `Pred` argument `a~i~` is initialized with `Pred{e}`. + NOTE: See also: <>. + -* If `a~i~` is of type `dynamic_capture const &`, `try_handle_some` is always able to produce it. It captures error objects which would otherwise have been discarded. -+ -.Example: -[source,c++] ----- -.... -try_handle_some( - - [] - { - return f(); // returns leaf::result - }, - - [](leaf::dynamic_capture const & cap) -> leaf::result <1> - { - return cap; <2> - } ); ----- -+ -<1> This handler matches any error. -<2> In this typical use, the contents of `cap` are captured in the returned `leaf::result`. -+ -[.text-right] -<> | <> -+ * If `a~i~` is of type `error_info const &`, `try_handle_some` is always able to produce it. + .Example: @@ -3618,11 +3599,11 @@ namespace boost { namespace leaf { } } ---- -Requires: :: `!<>()`. +Requires: :: `!<>()`. Effects: :: Associates `*this` with the calling thread. -Ensures: :: `<>()`. +Ensures: :: `<>()`. When a context is associated with a thread, thread-local pointers are set to point each `E...` type in its store, while the previous value of each such pointer is preserved in the `context` object, so that the effect of `activate` can be undone by calling `deactivate`. @@ -3645,12 +3626,12 @@ namespace boost { namespace leaf { ---- Requires: :: -* `<>()`; +* `<>()`; * `*this` must be the last activated `context` object in the calling thread. Effects: :: Un-associates `*this` with the calling thread. -Ensures: :: `!<>()`. +Ensures: :: `!<>()`. When a context is deactivated, the thread-local pointers that currently point to each individual error object storage in it are restored to their original value prior to calling <>. @@ -3735,7 +3716,7 @@ namespace boost { namespace leaf { ---- Requires: :: -`!<>()`. +`!<>()`. Effects: :: @@ -3812,139 +3793,6 @@ The behavior of `diagnostic_info` (and <>) is affected * If it is 0, the `diagnostic_info` functionality is stubbed out even for error handling contexts that take an argument of type `diagnostic_info`. This could shave a few cycles off the error path in some programs (but it is probably not worth it). -- -''' - -[[dynamic_capture]] -=== `dynamic_capture` - -.#include -[source,c++] ----- -#if BOOST_LEAF_CFG_CAPTURE - -namespace boost { namespace leaf { - - class dynamic_capture - { - //Constructors unspecified - - public: - - bool empty() const noexcept; - - int size() const noexcept; - - template - operator result() const noexcept; - - template - friend std::ostream & operator<<( std::basic_ostream &, dynamic_capture const & ); - }; - -} } - -#endif ----- - -By default, error objects reported via LEAF which are not taken as arguments by any active handler are discarted. If an active handler exists which takes an argument of type `dynamic_capture const &`, that object collects the error objects that would otherwise have been discarded, storing them on the heap. - -The message printed by `operator<<` includes diagnostic information about the captured error objects, except if `BOOST_LEAF_CFG_DIAGNOSTICS=0` is defined, in which case it does not. - -When an object of type `dynamic_capture const &` is returned from a handler that produces a `leaf::<>`, its contents are captured (together with the `std::current_exception`) by the returned `result` object, which can later be used with <> to handle the captured error objects. See <>. - -Under `BOOST_LEAF_CFG_CAPTURE=0`, `dynamic_capture` is unavailable. - -[WARNING] --- -A `dynamic_capture` does not capture all error objects, only the ones that would have been discarded if not for the presence of a `dynamic_capture` handler. In the example below, the returned `result` will capture the error object of type `E2` but not `e1`: -``` -auto r = try_handle_some( - []() -> leaf::result - { - return leaf::new_error(E1{}, E2{}); - }, - []( E1 e1, leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; - }); -``` -Perhaps more surprising, this is true for the next example as well: -``` -auto r = try_handle_some( - []() -> leaf::result - { - return try_handle_some( - []() -> leaf::result - { - return leaf::new_error(E1{}, E2{}); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; - }); - }, - []( E1 e1 ) - { - }); -``` --- - -See also: :: <>. - -''' - -[[dynamic_capture::empty]] -==== `empty` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - bool dynamic_capture::empty() const noexcept; - -} } ----- - -Returns: :: `true` if the `dynamic_capture` objects is empty, `false` otherwise. - -''' - -[[dynamic_capture::size]] -==== `size` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - int dynamic_capture::size() const noexcept; - -} } ----- - -Returns: :: The number of error objects captured by `*this`. - -''' - -[[dynamic_capture::operator_result]] -==== `operator result` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - operator result() const noexcept; - -} } ----- - -Effects: :: Implicit conversion to `leaf::<>`. The contents of `*this`, together with the `std::current_exception()` (if available) are moved and stored in the returned `result` object, which can be stashed away to be used at a later time with <> / <> / <> to handle the captured errors. - -''' - [[error_id]] === `error_id` @@ -4026,7 +3874,7 @@ TIP: To check if a given `std::error_code` is actually carrying an `error_id`, u Typically, users create new `error_id` objects by invoking <>. The constructor that takes `std::error_code`, and the one that takes a type `Enum` for which `std::is_error_code_enum::value` is `true`, have the following effects: * If `ec.value()` is `0`, the effect is the same as using the default constructor. -* Otherwise, if `<>(ec)` is `true`, the original `error_id` value is used to initialize `*this`; +* Otherwise, if `<>(ec)` is `true`, the original `error_id` value is used to initialize `*this`; * Otherwise, `*this` is initialized by the value returned by <>, while `ec` is passed to `load`, which enables handlers used with `try_handle_some`, `try_handle_all` or `try_catch` to receive it as an argument of type `std::error_code`. ''' @@ -4519,7 +4367,7 @@ Requires: :: `T` must be movable, and its move constructor may not throw. Invariant: :: A `result` object is in one of three states: * Value state, in which case it contains an object of type `T`, and <> / <> / <> can be used to access the contained value. * Error state, in which case it contains an error ID, and calling <> throws `leaf::bad_result`. -* Dynamic capture state, which is the same as the Error state, but in addition to the error ID, it holds a list of dynamically captured error objects; see <>. +* Dynamic capture state, which is the same as the Error state, but in addition to the error ID, it holds a list of dynamically captured error objects; see <>. `result` objects are nothrow-moveable but are not copyable. @@ -4584,7 +4432,7 @@ CAUTION: Initializing a `result` with a default-initialized `error_id` object + ** a `std::error_code` object. ** an object of type `Enum` for which `std::is_error_code_enum::value` is `true`. -* To get a `result` in <>, write an error handler that takes a <> object and returns a `result` object, and return the capture object from the error handler. +* To get a `result` in <>, call <>. -- + When a `result` object is initialized with a `std::error_code` object, it is used to initialize an `error_id` object, then the behavior is the same as if initialized with `error_id`. @@ -4752,9 +4600,9 @@ Effects: * If `*this` is in <>, returns a reference to the stored value. * If `*this` is in <>, the captured error objects are unloaded, and: -** If `*this` contains a captured exception object `ex`, the behavior is equivalent to `<>(ex)`. -** Otherwise, the behavior is equivalent to `<>(bad_result{})`. -* If `*this` is in any other state, the behavior is equivalent to `<>(bad_result{})`. +** If `*this` contains a captured exception object `ex`, the behavior is equivalent to `<>(ex)`. +** Otherwise, the behavior is equivalent to `<>(bad_result{})`. +* If `*this` is in any other state, the behavior is equivalent to `<>(bad_result{})`. ''' @@ -4864,7 +4712,7 @@ The following predicates are available: * <> * <> -In addition, any user-defined type `Pred` for which `<>::value` is `true` is treated as a predicate. In this case, it is required that: +In addition, any user-defined type `Pred` for which `<>::value` is `true` is treated as a predicate. In this case, it is required that: * `Pred` defines an accessible member type `error_type` to specify the error object type it requires; * `Pred` defines an accessible static member function `evaluate`, which returns a boolean type, and can be invoked with an object of type `error_type const &`; @@ -5399,7 +5247,7 @@ The error handling functionality provided by <> and <` type R, you must specialize the `is_result_type` template so that `is_result_type::value` evaluates to `true`. -Naturally, the provided `leaf::<>` class template satisfies these requirements. In addition, it allows error objects to be transported across thread boundaries, using a <>. +Naturally, the provided `leaf::<>` class template satisfies these requirements. In addition, it allows error objects to be transported across thread boundaries, using a <>. [[macros]] == Reference: Macros @@ -5551,7 +5399,7 @@ If `BOOST_LEAF_CFG_GNUC_STMTEXPR` is `1` (which is the default under `pass:[__GN #define BOOST_LEAF_THROW_EXCEPTION <> ---- -Effects: :: `BOOST_LEAF_THROW_EXCEPTION(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically communicated with the thrown exception, in a `<>` object (in addition to all `e...` objects). +Effects: :: `BOOST_LEAF_THROW_EXCEPTION(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically communicated with the thrown exception, in a `<>` object (in addition to all `e...` objects). ''' @@ -5564,7 +5412,7 @@ Effects: :: `BOOST_LEAF_THROW_EXCEPTION(e...)` is equivalent to `leaf::<> ---- -Effects: :: `BOOST_LEAF_NEW_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed, in a `<>` object (in addition to all `e...` objects). +Effects: :: `BOOST_LEAF_NEW_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed, in a `<>` object (in addition to all `e...` objects). [[configuration]] == Configuration @@ -5577,7 +5425,7 @@ The following configuration macros are recognized: * `BOOST_LEAF_CFG_STD_STRING`: Defining this macro as `0` disables all use of `std::string` (this requires `BOOST_LEAF_CFG_DIAGNOSTICS=0` as well). In this case LEAF does not `#include ` which may be too heavy for embedded platforms (if the macro is left undefined, LEAF defines it as `1`). -* `BOOST_LEAF_CFG_CAPTURE`: Defining this macro as `0` disables the <> functionality, which (only if used) allocates memory dynamically (if the macro is left undefined, LEAF defines it as `1`). +* `BOOST_LEAF_CFG_CAPTURE`: Defining this macro as `0` disables <>, which (only if used) allocates memory dynamically (if the macro is left undefined, LEAF defines it as `1`). * `BOOST_LEAF_CFG_GNUC_STMTEXPR`: This macro controls whether or not <> is defined in terms of a https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html[GNU C statement expression], which enables its use to check for errors similarly to how the questionmark operator works in some languages (see <>). By default the macro is defined as `1` under `pass:[__GNUC__]`, otherwise as `0`. @@ -5795,7 +5643,7 @@ leaf::try_catch( [.text-right] <> | <> | <> -Similar syntax works without exception handling as well. Below is the same snippet, written using `<>`: +Similar syntax works without exception handling as well. Below is the same snippet, written using `<>`: [source,c++] ---- @@ -6006,9 +5854,9 @@ leaf::try_catch( [WARNING] ==== -The fact that Boost Exception stores all supplied `boost::error_info` objects -- while LEAF discards them if they aren't needed -- affects the completeness of the message we get when we print `leaf::<>` objects, compared to the string returned by https://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[`boost::diagnostic_information`]. +The fact that Boost Exception stores all supplied `boost::error_info` objects -- while LEAF discards them if they aren't needed -- affects the completeness of the message we get when we print `leaf::<` objects, compared to the string returned by https://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[`boost::diagnostic_information`]. -If the user requires a complete diagnostic message, the solution is to use `leaf::<>`. In this case, before unused error objects are discarded by LEAF, they are converted to string and printed. Note that this allocates memory dynamically. +If the user requires a complete diagnostic message, the solution is to use `leaf::<>`. In this case, before unused error objects are discarded by LEAF, they are converted to string and printed. Note that this allocates memory dynamically. ==== ''' diff --git a/doc/zajo-dark.css b/doc/zajo-dark.css index 5d7986a..4247e85 100644 --- a/doc/zajo-dark.css +++ b/doc/zajo-dark.css @@ -448,7 +448,7 @@ a:hover{color:#00cc99} a:focus{color:#FFFFFF} h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Quicksand","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#00cc99;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.4em} code{font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;font-weight:400;color:black} -*:not(pre)>code{font-size:1.08em;font-style:normal!important;letter-spacing:0;padding:0 0;word-spacing:-.15em;background-color:transparent;-webkit-border-radius:0;border-radius:0;line-height:1.45;text-rendering:optimizeLegibility;word-wrap:break-word;color:white} +*:not(pre)>code{font-size:1.0em;font-style:normal!important;letter-spacing:0;padding:0 0;word-spacing:-.15em;background-color:transparent;-webkit-border-radius:0;border-radius:0;line-height:1.45;text-rendering:optimizeLegibility;word-wrap:break-word;color:white} pre,pre>code{line-height:1.45;color:rgba(255,255,255,.67);font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeLegibility;font-size:1.05em;background-color:#101010} a:not(pre)>code:hover {color:#00cc99} kbd{font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap} diff --git a/doc/zajo-light.css b/doc/zajo-light.css index ac0dd29..89a5cda 100644 --- a/doc/zajo-light.css +++ b/doc/zajo-light.css @@ -442,7 +442,7 @@ a:hover{color:#4101a7} a:focus{color:#000000} h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Quicksand","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#4101a7;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.4em} code{font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;font-weight:400;color:black} -*:not(pre)>code{font-size:1.08em;font-style:normal!important;letter-spacing:0;padding:0 0;word-spacing:-.15em;background-color:transparent;-webkit-border-radius:0;border-radius:0;line-height:1.45;text-rendering:optimizeLegibility;word-wrap:break-word} +*:not(pre)>code{font-size:1.0em;font-style:normal!important;letter-spacing:0;padding:0 0;word-spacing:-.15em;background-color:transparent;-webkit-border-radius:0;border-radius:0;line-height:1.45;text-rendering:optimizeLegibility;word-wrap:break-word} pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeLegibility;font-size:1.05em;background-color:#f7f8f7} a:not(pre)>code:hover {color:#4101a7} kbd{font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap} diff --git a/example/dynamic_capture_eh.cpp b/example/dynamic_capture_eh.cpp index 7b34a83..0a78767 100644 --- a/example/dynamic_capture_eh.cpp +++ b/example/dynamic_capture_eh.cpp @@ -57,14 +57,10 @@ int main() std::launch::async, [&] { - return leaf::try_catch( - [&]() -> leaf::result + return leaf::try_capture_all( + [&] { return task(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); } ); diff --git a/example/dynamic_capture_result.cpp b/example/dynamic_capture_result.cpp index bf3374c..f99875c 100644 --- a/example/dynamic_capture_result.cpp +++ b/example/dynamic_capture_result.cpp @@ -57,14 +57,10 @@ int main() std::launch::async, [&] { - return leaf::try_handle_some( - [&]() -> leaf::result + return leaf::try_capture_all( + [&] { return task(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); } ); diff --git a/include/boost/leaf/capture.hpp b/include/boost/leaf/capture.hpp new file mode 100644 index 0000000..8716df1 --- /dev/null +++ b/include/boost/leaf/capture.hpp @@ -0,0 +1,121 @@ +#ifndef BOOST_LEAF_CAPTURE_HPP_INCLUDED +#define BOOST_LEAF_CAPTURE_HPP_INCLUDED + +// Copyright 2018-2023 Emil Dotchevski and Reverge Studios, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#if BOOST_LEAF_CFG_CAPTURE + +namespace boost { namespace leaf { + +namespace leaf_detail +{ + template ::value> + struct is_result_tag; + + template + struct is_result_tag + { + }; + + template + struct is_result_tag + { + }; +} + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace leaf_detail +{ + template + inline + decltype(std::declval()(std::forward(std::declval())...)) + capture_impl(is_result_tag, F && f, A... a) noexcept + { + return std::forward(f)(std::forward(a)...); + } + + template + inline + decltype(std::declval().get()) + future_get_impl(is_result_tag, Future & fut) noexcept + { + return fut.get(); + } +} + +#else + +namespace leaf_detail +{ + // Not defined, no longer supported. Please use try_capture_all instead of make_shared_context/capture. + template + decltype(std::declval()(std::forward(std::declval())...)) + capture_impl(is_result_tag, F && f, A... a); + + // Not defined, no longer supported. Please use try_capture_all instead of make_shared_context/capture. + template + decltype(std::declval().get()) + future_get_impl(is_result_tag, Future & fut ); +} + +#endif + +namespace leaf_detail +{ + template + inline + decltype(std::declval()(std::forward(std::declval())...)) + capture_impl(is_result_tag, F && f, A... a) noexcept + { + return try_capture_all( + [&] + { + return std::forward(f)(std::forward(a)...); + } ); + } + + template + inline + decltype(std::declval().get()) + future_get_impl(is_result_tag, Future & fut) noexcept + { + if( auto r = fut.get() ) + return r; + else + { + r.unload(); + return r; + } + } +} + +template +inline +decltype(std::declval()(std::forward(std::declval())...)) +capture(context_ptr &&, F && f, A... a) +{ + using namespace leaf_detail; + return capture_impl(is_result_tag()(std::forward(std::declval())...))>(), std::forward(f), std::forward(a)...); +} + +template +inline +decltype(std::declval().get()) +future_get( Future & fut ) +{ + using namespace leaf_detail; + return future_get_impl(is_result_tag().get())>(), fut); +} + +} } + +#endif + +#endif diff --git a/include/boost/leaf/config.hpp b/include/boost/leaf/config.hpp index 8b71251..dadd915 100644 --- a/include/boost/leaf/config.hpp +++ b/include/boost/leaf/config.hpp @@ -235,6 +235,16 @@ //////////////////////////////////////// +#ifndef BOOST_LEAF_DEPRECATED +# if __cplusplus > 201402L +# define BOOST_LEAF_DEPRECATED(msg) [[deprecated(msg)]] +# else +# define BOOST_LEAF_DEPRECATED(msg) +# endif +#endif + +//////////////////////////////////////// + #ifndef BOOST_LEAF_NO_EXCEPTIONS # include # if (defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L) || (defined(_MSC_VER) && _MSC_VER >= 1900) diff --git a/include/boost/leaf/context.hpp b/include/boost/leaf/context.hpp index 49040dd..4668006 100644 --- a/include/boost/leaf/context.hpp +++ b/include/boost/leaf/context.hpp @@ -18,7 +18,6 @@ namespace boost { namespace leaf { class error_info; class diagnostic_info; class verbose_diagnostic_info; -class dynamic_capture; template struct is_predicate: std::false_type @@ -60,7 +59,6 @@ namespace leaf_detail static_assert(!std::is_same::value, "Handlers must take leaf::error_info arguments by const &"); static_assert(!std::is_same::value, "Handlers must take leaf::diagnostic_info arguments by const &"); static_assert(!std::is_same::value, "Handlers must take leaf::verbose_diagnostic_info arguments by const &"); - static_assert(!std::is_same::value, "Handlers must take leaf::dynamic_capture arguments by const &"); }; template @@ -383,6 +381,26 @@ BOOST_LEAF_CONSTEXPR inline context_type_from_handlers make_context( H && return { }; } +//////////////////////////////////////////// + +#if BOOST_LEAF_CFG_CAPTURE + +template +BOOST_LEAF_DEPRECATED("Please use try_capture_all instead of make_shared_context/capture.") +inline context_ptr make_shared_context() noexcept +{ + return std::make_shared(); +} + +template +BOOST_LEAF_DEPRECATED("Please use try_capture_all instead of make_shared_context/capture.") +inline context_ptr make_shared_context( H && ... ) noexcept +{ + return std::make_shared(); +} + +#endif + } } #endif diff --git a/include/boost/leaf/detail/all.hpp b/include/boost/leaf/detail/all.hpp index 369b0c1..769cfd8 100644 --- a/include/boost/leaf/detail/all.hpp +++ b/include/boost/leaf/detail/all.hpp @@ -4,7 +4,9 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include +#include #include #include #include diff --git a/include/boost/leaf/detail/capture_list.hpp b/include/boost/leaf/detail/capture_list.hpp index ec0beb8..a404792 100644 --- a/include/boost/leaf/detail/capture_list.hpp +++ b/include/boost/leaf/detail/capture_list.hpp @@ -93,11 +93,6 @@ namespace leaf_detail } ); } - bool empty() const noexcept - { - return first_ == nullptr; - } - template void print( std::basic_ostream & os, char const * title, int const err_id_to_print ) const { diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 2e2d006..b53794f 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -70,9 +70,6 @@ namespace boost { namespace leaf { class BOOST_LEAF_SYMBOL_VISIBLE error_id; -template -class BOOST_LEAF_SYMBOL_VISIBLE result; - namespace leaf_detail { struct BOOST_LEAF_SYMBOL_VISIBLE tls_tag_id_factory_current_id; @@ -373,19 +370,8 @@ namespace leaf_detail } ); } - int size() const noexcept - { - int c = 0; - for_each( - [&]( capture_list::node const & ) - { - ++c; - } ); - return c; - } - - template - result extract_capture_list() noexcept + template + LeafResult extract_capture_list() noexcept { #ifndef BOOST_LEAF_NO_EXCEPTIONS if( std::exception_ptr ex = std::current_exception() ) @@ -397,7 +383,6 @@ namespace leaf_detail return { err_id_, capture_list(f) }; } - using capture_list::empty; using capture_list::print; }; @@ -810,6 +795,18 @@ inline error_id current_error() noexcept return leaf_detail::make_error_id(leaf_detail::current_id()); } +//////////////////////////////////////////// + +class polymorphic_context +{ +}; + +#if BOOST_LEAF_CFG_CAPTURE +using context_ptr = std::shared_ptr; +#endif + +//////////////////////////////////////////// + template class context_activator { diff --git a/include/boost/leaf/handle_errors.hpp b/include/boost/leaf/handle_errors.hpp index 2ab3b76..518a732 100644 --- a/include/boost/leaf/handle_errors.hpp +++ b/include/boost/leaf/handle_errors.hpp @@ -336,84 +336,6 @@ namespace leaf_detail //////////////////////////////////////// -#if BOOST_LEAF_CFG_CAPTURE - -class dynamic_capture -{ - template - friend class ::boost::leaf::result; - - dynamic_capture( dynamic_capture const & ) = delete; - dynamic_capture & operator=( dynamic_capture const & ) = delete; - - leaf_detail::dynamic_allocator * const da_; - error_id err_; - -protected: - - dynamic_capture( dynamic_capture && ) = default; - - BOOST_LEAF_CONSTEXPR dynamic_capture( leaf_detail::dynamic_allocator * da, error_id err ) noexcept: - da_(da), - err_(err) - { - } - -public: - - BOOST_LEAF_CONSTEXPR bool empty() const noexcept - { - return !da_ || da_->empty(); - } - - BOOST_LEAF_CONSTEXPR int size() const noexcept - { - return da_ ? da_->size() : 0; - } - - template - operator result() const noexcept - { - if( da_ ) - return da_->extract_capture_list(); - else - return { error_id() }; - } - - template - friend std::ostream & operator<<( std::basic_ostream & os, dynamic_capture const & x ) - { - if( x.da_ ) - x.da_->print(os, "Captured error objects:\n", x.err_.value()); - return os; - } -}; - -namespace leaf_detail -{ - struct dynamic_capture_: dynamic_capture - { - BOOST_LEAF_CONSTEXPR dynamic_capture_( leaf_detail::dynamic_allocator * da, error_id err ) noexcept: - dynamic_capture(da, err) - { - } - }; - - template <> - struct handler_argument_traits: handler_argument_always_available - { - template - BOOST_LEAF_CONSTEXPR static dynamic_capture_ get( Tup & tup, error_info const & ei ) noexcept - { - return dynamic_capture_(handler_argument_traits_defaults::check(tup, ei), ei.error()); - } - }; -} - -#endif - -//////////////////////////////////////// - namespace leaf_detail { template @@ -994,6 +916,113 @@ try_catch( TryBlock && try_block, H && ... h ) #endif +#if BOOST_LEAF_CFG_CAPTURE + +namespace leaf_detail +{ + template + struct try_capture_all_dispatch_non_void + { + using leaf_result = LeafResult; + + template + inline + static + leaf_result + try_capture_all_( TryBlock && try_block ) noexcept + { + leaf_detail::slot sl; + sl.activate(); +#ifndef BOOST_LEAF_NO_EXCEPTIONS + try +#endif + { + if( leaf_result r = std::forward(try_block)() ) + { + sl.deactivate(); + return r; + } + else + { + sl.deactivate(); + return leaf_result(sl.value(error_id(r.error()).value()).template extract_capture_list()); + } + } +#ifndef BOOST_LEAF_NO_EXCEPTIONS + catch( std::exception & ex ) + { + sl.deactivate(); + return sl.value(error_info(&ex).error().value()).template extract_capture_list(); + } + catch(...) + { + sl.deactivate(); + return sl.value(error_info(nullptr).error().value()).template extract_capture_list(); + } +#endif + } + }; + + template ::value, bool IsResultType = is_result_type::value> + struct try_capture_all_dispatch; + + template + struct try_capture_all_dispatch: + try_capture_all_dispatch_non_void<::boost::leaf::result().value())>::type>> + { + }; + + template + struct try_capture_all_dispatch: + try_capture_all_dispatch_non_void<::boost::leaf::result::type>> + { + }; + + template + struct try_capture_all_dispatch + { + using leaf_result = ::boost::leaf::result; + + template + inline + static + leaf_result + try_capture_all_( TryBlock && try_block ) noexcept + { + leaf_detail::slot sl; + sl.activate(); +#ifndef BOOST_LEAF_NO_EXCEPTIONS + try +#endif + { + std::forward(try_block)(); + return {}; + } +#ifndef BOOST_LEAF_NO_EXCEPTIONS + catch( std::exception & ex ) + { + sl.deactivate(); + return sl.value(error_info(&ex).error().value()).template extract_capture_list(); + } + catch(...) + { + sl.deactivate(); + return sl.value(error_info(nullptr).error().value()).template extract_capture_list(); + } +#endif + } + }; +} + +template +inline +typename leaf_detail::try_capture_all_dispatch()())>::leaf_result +try_capture_all( TryBlock && try_block ) noexcept +{ + return leaf_detail::try_capture_all_dispatch()())>::try_capture_all_(std::forward(try_block)); +} +#endif + } } // Boost Exception Integration diff --git a/meson.build b/meson.build index 1f6b1f8..b2f3ec3 100644 --- a/meson.build +++ b/meson.build @@ -119,6 +119,7 @@ if option_enable_unit_tests 'capture_exception_result_unload_test', 'capture_exception_state_test', 'capture_exception_unload_test', + 'capture_result_async_test_', 'capture_result_async_test', 'capture_result_state_test', 'context_activator_test', @@ -140,7 +141,6 @@ if option_enable_unit_tests 'diagnostic_info_test3', 'diagnostic_info_test4', 'diagnostic_info_test5', - 'dynamic_capture_print_test', 'dynamic_capture_test', 'e_errno_test', 'e_LastError_test', diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 696495d..b58f4a4 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -59,6 +59,7 @@ run capture_exception_result_async_test.cpp ; run capture_exception_result_unload_test.cpp ; run capture_exception_state_test.cpp ; run capture_exception_unload_test.cpp ; +run capture_result_async_test_.cpp ; run capture_result_async_test.cpp ; run capture_result_state_test.cpp ; run context_activator_test.cpp ; @@ -80,7 +81,6 @@ run diagnostic_info_test2.cpp ; run diagnostic_info_test3.cpp ; run diagnostic_info_test4.cpp ; run diagnostic_info_test5.cpp ; -run dynamic_capture_print_test.cpp ; run dynamic_capture_test.cpp ; run e_errno_test.cpp ; run e_LastError_test.cpp ; diff --git a/test/capture_exception_async_test.cpp b/test/capture_exception_async_test.cpp index f5871f2..507022f 100644 --- a/test/capture_exception_async_test.cpp +++ b/test/capture_exception_async_test.cpp @@ -58,14 +58,10 @@ std::vector launch_tasks( int task_count, F f ) return fut_info { a, b, res, std::async( std::launch::async, [=] { - return leaf::try_handle_some( - [&]() -> leaf::result + return leaf::try_capture_all( + [&] { return f(a, b, res); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ) }; } ); @@ -78,7 +74,7 @@ int main() int received_a, received_b; auto task = - []( int a, int b, int res ) -> leaf::result + []( int a, int b, int res ) { if( res >= 0 ) return res; diff --git a/test/capture_exception_result_async_test.cpp b/test/capture_exception_result_async_test.cpp index 46ca00c..ea8f7c2 100644 --- a/test/capture_exception_result_async_test.cpp +++ b/test/capture_exception_result_async_test.cpp @@ -57,14 +57,10 @@ std::vector launch_tasks( int task_count, F f ) return fut_info { a, b, res, std::async( std::launch::async, [=] { - return leaf::try_handle_some( - [&]() -> leaf::result + return leaf::try_capture_all( + [&] { return f(a, b, res); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ) }; } ); @@ -82,7 +78,7 @@ int main() if( res >= 0 ) return res; else - return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); + return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); }; auto error_handlers = std::make_tuple( @@ -130,7 +126,7 @@ int main() f.fut.wait(); received_a = received_b = 0; int r = leaf::try_handle_all( - [&]() -> leaf::result + [&] { auto load = leaf::on_error( info<4>{} ); diff --git a/test/capture_exception_result_unload_test.cpp b/test/capture_exception_result_unload_test.cpp index 542cde5..99bde9d 100644 --- a/test/capture_exception_result_unload_test.cpp +++ b/test/capture_exception_result_unload_test.cpp @@ -127,83 +127,59 @@ int main() { test( [] { - return leaf::try_catch( - []() -> leaf::result + return leaf::try_capture_all( + []() -> int { leaf::throw_exception(errc_a::a0, info<1>{1}, info<3>{3}); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_catch( - []() -> leaf::result + return leaf::try_capture_all( + []() -> void { leaf::throw_exception(errc_a::a0, info<1>{1}, info<3>{3}); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_catch( - []() -> leaf::result + return leaf::try_capture_all( + []() -> int { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); leaf::throw_exception(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_catch( - []() -> leaf::result + return leaf::try_capture_all( + []() -> void { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); leaf::throw_exception(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_catch( - []() -> leaf::result + return leaf::try_capture_all( + []() -> int { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); throw std::exception(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_catch( - []() -> leaf::result + return leaf::try_capture_all( + []() -> void { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); throw std::exception(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); diff --git a/test/capture_exception_state_test.cpp b/test/capture_exception_state_test.cpp index e5bc519..7dee128 100644 --- a/test/capture_exception_state_test.cpp +++ b/test/capture_exception_state_test.cpp @@ -64,17 +64,11 @@ struct info int main() { { - leaf::result r = leaf::try_handle_some( - []() -> leaf::result + leaf::result r = leaf::try_capture_all( + []() -> int { leaf::throw_exception(info<1>{}, info<3>{}); - }, - [](leaf::dynamic_capture const & cap) -> leaf::result - { - BOOST_TEST(!cap.empty()); - BOOST_TEST_EQ(cap.size(), 2); - return cap; - } ); + }); BOOST_TEST_EQ(count, 2); #if BOOST_LEAF_CFG_STD_STRING diff --git a/test/capture_exception_unload_test.cpp b/test/capture_exception_unload_test.cpp index 9be30b5..b7d3949 100644 --- a/test/capture_exception_unload_test.cpp +++ b/test/capture_exception_unload_test.cpp @@ -125,83 +125,59 @@ int main() { test( [] { - return leaf::try_handle_some( - []() -> leaf::result + return leaf::try_capture_all( + []() -> int { leaf::throw_exception(errc_a::a0, info<1>{1}, info<3>{3}); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_handle_some( - []() -> leaf::result + return leaf::try_capture_all( + []() -> void { leaf::throw_exception(errc_a::a0, info<1>{1}, info<3>{3}); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_handle_some( - []() -> leaf::result + return leaf::try_capture_all( + []() -> int { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); leaf::throw_exception(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_handle_some( - []() -> leaf::result + return leaf::try_capture_all( + []() -> void { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); leaf::throw_exception(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_handle_some( - []() -> leaf::result + return leaf::try_capture_all( + []() -> int { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); throw std::exception(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_handle_some( - []() -> leaf::result + return leaf::try_capture_all( + []() -> void { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); throw std::exception(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); diff --git a/test/capture_result_async_test.cpp b/test/capture_result_async_test.cpp index 75541fe..569ff5f 100644 --- a/test/capture_result_async_test.cpp +++ b/test/capture_result_async_test.cpp @@ -57,14 +57,10 @@ std::vector launch_tasks( int task_count, F f ) return fut_info { a, b, res, std::async( std::launch::async, [=] { - return leaf::try_handle_some( + return leaf::try_capture_all( [&]() -> leaf::result { return f(a, b, res); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ) }; } ); diff --git a/test/capture_result_async_test_.cpp b/test/capture_result_async_test_.cpp new file mode 100644 index 0000000..3b6dfba --- /dev/null +++ b/test/capture_result_async_test_.cpp @@ -0,0 +1,162 @@ +// Copyright 2018-2023 Emil Dotchevski and Reverge Studios, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#if defined(BOOST_LEAF_NO_THREADS) || !BOOST_LEAF_CFG_CAPTURE + +#include + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include +# include +# include +# include +#endif + +#include "lightweight_test.hpp" +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +struct fut_info +{ + int a; + int b; + int result; + std::future> fut; +}; + +template +std::vector launch_tasks( int task_count, F f ) +{ + BOOST_LEAF_ASSERT(task_count>0); + std::vector fut; + std::generate_n( std::back_inserter(fut), task_count, + [=] + { + int const a = rand(); + int const b = rand(); + int const res = (rand()%10) - 5; + return fut_info { a, b, res, std::async( std::launch::async, + [=] + { + return leaf::capture(leaf::make_shared_context(), f, a, b, res); + } ) }; + } ); + return fut; +} + +int main() +{ + int received_a, received_b; + auto error_handlers = std::make_tuple( + [&received_a, &received_b]( info<1> const & x1, info<2> const & x2, info<4> const & ) + { + received_a = x1.value; + received_b = x2.value; + return -1; + }, + [] + { + return -2; + } ); + + { + std::vector fut = launch_tasks( + 100, + []( int a, int b, int res ) -> leaf::result + { + if( res >= 0 ) + return res; + else + return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); + } ); + + for( auto & f : fut ) + { + f.fut.wait(); + int r = leaf::try_handle_all( + [&] + { + auto load = leaf::on_error( info<4>{} ); + + // Calling future_get is required in order to make the on_error (above) work. + return leaf::future_get(f.fut); + }, + error_handlers ); + if( f.result>=0 ) + BOOST_TEST_EQ(r, f.result); + else + { + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(f.a, received_a); + BOOST_TEST_EQ(f.b, received_b); + } + } + } + + { + std::vector fut = launch_tasks( + 100, + []( int a, int b, int res ) -> leaf::result + { + if( res >= 0 ) + return res; + else + return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); + } ); + + for( auto & f : fut ) + { + f.fut.wait(); + int r = leaf::try_handle_all( + [&] + { + auto load = leaf::on_error( info<4>{} ); + + return leaf::try_handle_some( + [&] + { + // Not calling future_get, a on_error in this scope won't work correctly. + // This is to verify that the on_error in the outer scope (above) works. + return f.fut.get(); + }, + []( leaf::error_info const & err ) + { + return err.error(); + } ); + }, + error_handlers ); + if( f.result>=0 ) + BOOST_TEST_EQ(r, f.result); + else + { + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(f.a, received_a); + BOOST_TEST_EQ(f.b, received_b); + } + } + } + + return boost::report_errors(); +} + +#endif diff --git a/test/capture_result_state_test.cpp b/test/capture_result_state_test.cpp index 7e1461a..bde3d2d 100644 --- a/test/capture_result_state_test.cpp +++ b/test/capture_result_state_test.cpp @@ -63,16 +63,10 @@ struct info int main() { { - leaf::result r = leaf::try_handle_some( + leaf::result r = leaf::try_capture_all( []() -> leaf::result { return leaf::new_error( info<1>{}, info<3>{} ); - }, - [](leaf::dynamic_capture const & cap) -> leaf::result - { - BOOST_TEST(!cap.empty()); - BOOST_TEST_EQ(cap.size(), 2); - return cap; } ); BOOST_TEST_EQ(count, 2); diff --git a/test/capture_result_unload_test.cpp b/test/capture_result_unload_test.cpp index bc988a8..6f9f9a4 100644 --- a/test/capture_result_unload_test.cpp +++ b/test/capture_result_unload_test.cpp @@ -126,55 +126,39 @@ int main() { test( [] { - return leaf::try_handle_some( + return leaf::try_capture_all( []() -> leaf::result { return leaf::new_error(errc_a::a0, info<1>{1}, info<3>{3}); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_handle_some( + return leaf::try_capture_all( []() -> leaf::result { return leaf::new_error(errc_a::a0, info<1>{1}, info<3>{3}); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_handle_some( + return leaf::try_capture_all( []() -> leaf::result { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); return leaf::new_error(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); test( [] { - return leaf::try_handle_some( + return leaf::try_capture_all( []() -> leaf::result { auto load = leaf::on_error(errc_a::a0, info<1>{1}, info<3>{3}); return leaf::new_error(); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); } ); diff --git a/test/dynamic_capture_print_test.cpp b/test/dynamic_capture_print_test.cpp deleted file mode 100644 index 9b48040..0000000 --- a/test/dynamic_capture_print_test.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018-2023 Emil Dotchevski and Reverge Studios, Inc. - -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include - -#if !BOOST_LEAF_CFG_CAPTURE || !BOOST_LEAF_CFG_STD_STRING || !BOOST_LEAF_CFG_DIAGNOSTICS - -#include - -int main() -{ - std::cout << "Unit test not applicable." << std::endl; - return 0; -} - -#else - -#ifdef BOOST_LEAF_TEST_SINGLE_HEADER -# include "leaf.hpp" -#else -# include -# include -#endif - -#if BOOST_LEAF_CFG_STD_STRING -# include -# include -#endif - -#include "lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct non_printable_value -{ -}; - -struct e_err -{ - template - friend std::ostream & operator<<( std::basic_ostream & os, e_err const & ) - { - return os << "e_err"; - } -}; - -int main() -{ - leaf::try_handle_all( - []() -> leaf::result - { - return leaf::new_error(e_err{ }); - }, - [&]( leaf::dynamic_capture const & cap ) - { - std::ostringstream st; - st << cap; - std::string s = st.str(); - std::cout << s << std::endl; - BOOST_TEST_NE(s.find("Captured error objects"), s.npos); - BOOST_TEST_NE(s.find("e_err"), s.npos); - } ); - - return boost::report_errors(); -} - -#endif diff --git a/test/dynamic_capture_test.cpp b/test/dynamic_capture_test.cpp index 08f9fb7..00c50a7 100644 --- a/test/dynamic_capture_test.cpp +++ b/test/dynamic_capture_test.cpp @@ -69,60 +69,14 @@ namespace int main() { { - leaf::try_handle_all( + leaf::result r = leaf::try_capture_all( []() -> leaf::result { return leaf::new_error(err<1>{}, err<2>{}); - }, - [&]( err<1>, err<2> &, leaf::dynamic_capture const & cap ) - { - BOOST_TEST_EQ(err<1>::count, 2); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(cap.size(), 0); - BOOST_TEST(cap.empty()); - }, - [] - { - BOOST_TEST(false); }); - BOOST_TEST_EQ(err<1>::count, 0); - BOOST_TEST_EQ(err<2>::count, 0); - } - - { - leaf::try_handle_all( - []() -> leaf::result - { - return leaf::new_error(err<1>{}, err<2>{}); - }, - [&]( leaf::dynamic_capture const & cap ) - { - BOOST_TEST_EQ(err<1>::count, 1); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(cap.size(), 2); - BOOST_TEST(!cap.empty()); - } ); - BOOST_TEST_EQ(err<1>::count, 0); - BOOST_TEST_EQ(err<2>::count, 0); - } - - { - leaf::result r = leaf::try_handle_some( - []() -> leaf::result - { - return leaf::new_error(err<1>{}, err<2>{}); - }, - [&]( leaf::dynamic_capture const & cap ) -> leaf::result - { - BOOST_TEST_EQ(err<1>::count, 1); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(cap.size(), 2); - BOOST_TEST(!cap.empty()); - return cap; - } ); - BOOST_TEST(!r); BOOST_TEST_EQ(err<1>::count, 1); BOOST_TEST_EQ(err<2>::count, 1); + BOOST_TEST(!r); int r1 = leaf::try_handle_all( [&]() -> leaf::result { @@ -143,19 +97,11 @@ int main() BOOST_TEST_EQ(err<2>::count, 0); { - leaf::result r = leaf::try_handle_some( + leaf::result r = leaf::try_capture_all( []() -> leaf::result { return leaf::new_error(err<1>{}, err<2>{}); - }, - [&]( leaf::dynamic_capture const & cap ) -> leaf::result - { - BOOST_TEST_EQ(err<1>::count, 1); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(cap.size(), 2); - BOOST_TEST(!cap.empty()); - return cap; - } ); + }); BOOST_TEST(!r); BOOST_TEST_EQ(err<1>::count, 1); BOOST_TEST_EQ(err<2>::count, 1); @@ -178,160 +124,6 @@ int main() BOOST_TEST_EQ(err<1>::count, 0); BOOST_TEST_EQ(err<2>::count, 0); - // nested/unload - { - leaf::result r = leaf::try_handle_some( - []() -> leaf::result - { - return leaf::try_handle_some( - []() -> leaf::result - { - return leaf::new_error(err<1>{}, err<2>{}, err<3>{}); - }, - []( err<3>, err<4> ) - { - BOOST_TEST(false); - }, - [&]( leaf::error_info const & ei, leaf::dynamic_capture const & cap ) - { - BOOST_TEST_EQ(err<1>::count, 1); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(err<3>::count, 1); - BOOST_TEST_EQ(cap.size(), 2); - BOOST_TEST(!cap.empty()); - err<1>::move_count = 0; - err<2>::move_count = 0; - return ei.error(); - } ); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - BOOST_TEST_EQ(err<1>::count, 1); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(err<3>::count, 1); - BOOST_TEST_EQ(cap.size(), 3); - BOOST_TEST(!cap.empty()); - return cap; - } ); - BOOST_TEST_EQ(err<1>::count, 1); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(err<3>::count, 1); - BOOST_TEST_EQ(err<1>::move_count, 0); - BOOST_TEST_EQ(err<2>::move_count, 0); - BOOST_TEST(!r); -#if BOOST_LEAF_CFG_STD_STRING - { - std::ostringstream st; - st << r; - std::string s = st.str(); - std::cout << s << std::endl; - if( BOOST_LEAF_CFG_DIAGNOSTICS ) - { - auto n1 = s.find("err<1>"); - auto n3 = s.find("err<3>"); - BOOST_TEST_NE(n1, s.npos); - BOOST_TEST_NE(n3, s.npos); - BOOST_TEST_LT(n1, n3); - } - } -#endif - int r1 = leaf::try_handle_all( - [&]() -> leaf::result - { - BOOST_LEAF_CHECK(r); - return 0; - }, - [](err<1>, err<2>) - { - return 1; - }, - [] - { - return 2; - } ); - BOOST_TEST_EQ(r1, 1); - } - BOOST_TEST_EQ(err<1>::count, 0); - BOOST_TEST_EQ(err<2>::count, 0); - BOOST_TEST_EQ(err<3>::count, 0); - - // nested/unload, different order - { - leaf::result r = leaf::try_handle_some( - []() -> leaf::result - { - return leaf::try_handle_some( - []() -> leaf::result - { - return leaf::new_error(err<1>{}, err<2>{}, err<3>{}); - }, - [&]( leaf::error_info const & ei, leaf::dynamic_capture const & cap ) - { - BOOST_TEST_EQ(err<1>::count, 1); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(err<3>::count, 1); - BOOST_TEST_EQ(cap.size(), 2); - BOOST_TEST(!cap.empty()); - err<1>::move_count = 0; - err<2>::move_count = 0; - return ei.error(); - }, - []( err<3>, err<4> ) - { - BOOST_TEST(false); - } ); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - BOOST_TEST_EQ(err<1>::count, 1); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(err<3>::count, 1); - BOOST_TEST_EQ(cap.size(), 3); - BOOST_TEST(!cap.empty()); - return cap; - } ); - BOOST_TEST_EQ(err<1>::count, 1); - BOOST_TEST_EQ(err<2>::count, 1); - BOOST_TEST_EQ(err<3>::count, 1); - BOOST_TEST_EQ(err<1>::move_count, 0); - BOOST_TEST_EQ(err<2>::move_count, 0); - BOOST_TEST(!r); -#if BOOST_LEAF_CFG_STD_STRING - { - std::ostringstream st; - st << r; - std::string s = st.str(); - std::cout << s << std::endl; - if( BOOST_LEAF_CFG_DIAGNOSTICS ) - { - auto n1 = s.find("err<1>"); - auto n3 = s.find("err<3>"); - BOOST_TEST_NE(n1, s.npos); - BOOST_TEST_NE(n3, s.npos); - BOOST_TEST_LT(n3, n1); - } - } -#endif - int r1 = leaf::try_handle_all( - [&]() -> leaf::result - { - BOOST_LEAF_CHECK(r); - return 0; - }, - [](err<1>, err<2>) - { - return 1; - }, - [] - { - return 2; - } ); - BOOST_TEST_EQ(r1, 1); - } - BOOST_TEST_EQ(err<1>::count, 0); - BOOST_TEST_EQ(err<2>::count, 0); - BOOST_TEST_EQ(err<3>::count, 0); - return boost::report_errors(); } diff --git a/test/result_print_test.cpp b/test/result_print_test.cpp index c1d70bf..789bd5a 100644 --- a/test/result_print_test.cpp +++ b/test/result_print_test.cpp @@ -89,14 +89,10 @@ int main() #if BOOST_LEAF_CFG_CAPTURE { - leaf::result r = leaf::try_handle_some( + leaf::result r = leaf::try_capture_all( []() -> leaf::result { return leaf::new_error(e_err{ }); - }, - []( leaf::dynamic_capture const & cap ) -> leaf::result - { - return cap; } ); #if BOOST_LEAF_CFG_STD_STRING std::ostringstream ss; diff --git a/test/result_state_test.cpp b/test/result_state_test.cpp index e05f473..d1c69c6 100644 --- a/test/result_state_test.cpp +++ b/test/result_state_test.cpp @@ -260,14 +260,10 @@ int main() #if BOOST_LEAF_CFG_CAPTURE { // error move -> capture -> move - leaf::result r1 = leaf::try_handle_some( + leaf::result r1 = leaf::try_capture_all( []()->leaf::result { return leaf::new_error(e_err{ }); - }, - [](leaf::dynamic_capture const & cap)->leaf::result - { - return cap; }); BOOST_TEST(!r1); BOOST_TEST_EQ(err::count, 1); @@ -282,15 +278,11 @@ int main() BOOST_TEST_EQ(err::count, 0); BOOST_TEST_EQ(val::count, 0); { // error copy -> capture -> move - leaf::result r1 = leaf::try_handle_some( + leaf::result r1 = leaf::try_capture_all( []()->leaf::result { leaf::error_id err = leaf::new_error( e_err{ } ); return leaf::result(err); - }, - [](leaf::dynamic_capture const & cap)->leaf::result - { - return cap; }); BOOST_TEST(!r1); BOOST_TEST_EQ(err::count, 1); @@ -306,14 +298,10 @@ int main() BOOST_TEST_EQ(val::count, 0); { // error move -> capture -> assign-move - leaf::result r1 = leaf::try_handle_some( + leaf::result r1 = leaf::try_capture_all( []()->leaf::result { return leaf::new_error(e_err{ }); - }, - [](leaf::dynamic_capture const & cap)->leaf::result - { - return cap; }); BOOST_TEST(!r1); BOOST_TEST_EQ(err::count, 1); @@ -328,15 +316,11 @@ int main() BOOST_TEST_EQ(err::count, 0); BOOST_TEST_EQ(val::count, 0); { // error copy -> capture -> assign-move - leaf::result r1 = leaf::try_handle_some( + leaf::result r1 = leaf::try_capture_all( []()->leaf::result { leaf::error_id err = leaf::new_error( e_err{ } ); return leaf::result(err); - }, - [](leaf::dynamic_capture const & cap)->leaf::result - { - return cap; }); BOOST_TEST(!r1); BOOST_TEST_EQ(err::count, 1); @@ -456,14 +440,10 @@ int main() #if BOOST_LEAF_CFG_CAPTURE { // void error move -> capture -> move - leaf::result r1 = leaf::try_handle_some( + leaf::result r1 = leaf::try_capture_all( []()->leaf::result { return leaf::new_error(e_err{ }); - }, - [](leaf::dynamic_capture const & cap)->leaf::result - { - return cap; }); BOOST_TEST(!r1); BOOST_TEST(r1.operator->() == 0); @@ -477,15 +457,11 @@ int main() } BOOST_TEST_EQ(err::count, 0); { // void error copy -> capture -> move - leaf::result r1 = leaf::try_handle_some( + leaf::result r1 = leaf::try_capture_all( []()->leaf::result { leaf::error_id err = leaf::new_error( e_err{ } ); return leaf::result(err); - }, - [](leaf::dynamic_capture const & cap)->leaf::result - { - return cap; }); BOOST_TEST(!r1); BOOST_TEST(r1.operator->() == 0); @@ -500,14 +476,10 @@ int main() BOOST_TEST_EQ(err::count, 0); { // void error move -> capture -> assign-move - leaf::result r1 = leaf::try_handle_some( + leaf::result r1 = leaf::try_capture_all( []()->leaf::result { return leaf::new_error(e_err{ }); - }, - [](leaf::dynamic_capture const & cap)->leaf::result - { - return cap; }); BOOST_TEST(!r1); BOOST_TEST(r1.operator->() == 0); @@ -521,15 +493,11 @@ int main() } BOOST_TEST_EQ(err::count, 0); { // void error copy -> capture -> assign-move - leaf::result r1 = leaf::try_handle_some( + leaf::result r1 = leaf::try_capture_all( []()->leaf::result { leaf::error_id err = leaf::new_error( e_err{ } ); return leaf::result(err); - }, - [](leaf::dynamic_capture const & cap)->leaf::result - { - return cap; }); BOOST_TEST(!r1); BOOST_TEST(r1.operator->() == 0);