diff --git a/include/boost/leaf/capture.hpp b/include/boost/leaf/capture.hpp new file mode 100644 index 00000000..8716df1e --- /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 8b712512..dadd915f 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 df54343b..46680060 100644 --- a/include/boost/leaf/context.hpp +++ b/include/boost/leaf/context.hpp @@ -381,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 369b0c1b..769cfd84 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/error.hpp b/include/boost/leaf/error.hpp index be382152..8876c171 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -807,6 +807,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/meson.build b/meson.build index 62e78a83..b2f3ec33 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', diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 8fe1acb9..b58f4a43 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 ; diff --git a/test/capture_result_async_test_.cpp b/test/capture_result_async_test_.cpp new file mode 100644 index 00000000..3b6dfba0 --- /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