diff --git a/include/ucoro/awaitable.hpp b/include/ucoro/awaitable.hpp index 7768162..4026e6e 100644 --- a/include/ucoro/awaitable.hpp +++ b/include/ucoro/awaitable.hpp @@ -168,6 +168,22 @@ namespace ucoro template using awaitable_return_type = template_parameter_of; + template + struct exception_with_result + { + using type = std::variant; + }; + + template<> + struct exception_with_result + { + using type = std::exception_ptr; + }; + + template + using exception_with_result_t = typename exception_with_result::type; + + } // namespace traits struct debug_coro_promise @@ -210,7 +226,7 @@ namespace ucoro value_.template emplace(std::current_exception()); } - T get_value() const + auto get_value() const { if (std::holds_alternative(value_)) { @@ -433,8 +449,7 @@ namespace ucoro auto detach() { - auto launch_coro = [](awaitable lazy) -> awaitable { co_return co_await std::move(lazy); }; - return launch_coro(std::move(*this)); + return detach(std::any{}); } auto detach(std::any local) @@ -452,6 +467,44 @@ namespace ucoro return launched_coro; } + template requires std::is_invocable_v> + auto detach_with_callback(Function completion_handler) + { + return detach_with_callback(std::any{}, completion_handler); + } + + template requires std::is_invocable_v> + auto detach_with_callback(std::any local, Function completion_handler) + { + auto launched_coro = [](awaitable lazy, auto completion_handler) mutable -> awaitable + { + using result_wrapper = ucoro::traits::exception_with_result_t; + try + { + if constexpr (std::is_void_v) + { + co_await std::move(lazy); + completion_handler(result_wrapper{nullptr}); + } + else + { + completion_handler(result_wrapper{co_await std::move(lazy)}); + } + } + catch(...) + { + completion_handler(result_wrapper{std::current_exception()}); + } + }(std::move(*this), std::move(completion_handler)); + + if (local.has_value()) + { + launched_coro.set_local(local); + } + + return launched_coro; + } + std::coroutine_handle current_coro_handle_; }; @@ -463,6 +516,7 @@ namespace ucoro auto result = awaitable{std::coroutine_handle>::from_promise(*this)}; return result; } + } // namespace ucoro ////////////////////////////////////////////////////////////////////////// @@ -598,9 +652,9 @@ namespace ucoro ////////////////////////////////////////////////////////////////////////// template -ucoro::CallbackAwaiter callback_awaitable(callback&& cb) +auto callback_awaitable(callback&& cb) -> ucoro::awaitable { - return ucoro::CallbackAwaiter{std::forward(cb)}; + co_return co_await ucoro::CallbackAwaiter{std::forward(cb)}; } template @@ -614,3 +668,30 @@ auto coro_start(Awaitable&& coro) { return coro.detach(); } + +template +auto sync_await(ucoro::awaitable lazy, std::any local_ = {}) -> T +{ + ucoro::traits::exception_with_result_t result; + + lazy.detach_with_callback(local_, [&](ucoro::traits::exception_with_result_t result_) + { + result = result_; + }); + + if constexpr (std::is_void_v) + { + if (result) + { + std::rethrow_exception(result); + } + } + else + { + if (std::holds_alternative(result)) + { + std::rethrow_exception(std::get(result)); + } + return std::get(result); + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ab9046d..f5fc5d9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,6 +3,7 @@ set_target_properties(ucoro_tests PROPERTIES FOLDER "ucoro_tests") add_subdirectory(test1) add_subdirectory(test2) +add_subdirectory(test7) find_package(Boost 1.60 COMPONENTS thread system atomic) if(Boost_FOUND) diff --git a/tests/test7/CMakeLists.txt b/tests/test7/CMakeLists.txt new file mode 100644 index 0000000..3581364 --- /dev/null +++ b/tests/test7/CMakeLists.txt @@ -0,0 +1,6 @@ + +add_executable(test7 test.cpp) +target_link_libraries(test7 ucoro) + +add_test(NAME test7 COMMAND test7) +set_target_properties(test7 PROPERTIES FOLDER "ucoro_tests") \ No newline at end of file diff --git a/tests/test7/test.cpp b/tests/test7/test.cpp new file mode 100644 index 0000000..52b9b24 --- /dev/null +++ b/tests/test7/test.cpp @@ -0,0 +1,45 @@ + +#include "ucoro/awaitable.hpp" +#include + +ucoro::awaitable test() +{ + throw std::runtime_error("test throw"); + co_return 1; +} + + +ucoro::awaitable test2() +{ + throw std::runtime_error("test throw"); + co_return; +} + +ucoro::awaitable coro_compute() +{ + try + { + sync_await(test2()); + } + catch(const std::exception& e) + { + std::cerr << "exception in test2: " << e.what() << '\n'; + } + + co_return co_await test(); +} + +int main(int argc, char** argv) +{ + try + { + std::string str = "hello"; + sync_await(coro_compute(), str); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << std::endl; + } + + return 0; +}