From 21d2f01386f7b7a38751779f85a18a85ec554383 Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Sun, 27 Oct 2024 16:35:47 +1100 Subject: [PATCH] Add disposition. --- asio/include/Makefile.am | 1 + asio/include/asio.hpp | 1 + asio/include/asio/detail/type_traits.hpp | 4 + asio/include/asio/disposition.hpp | 263 +++++++++++++++++++++++ asio/include/asio/impl/awaitable.hpp | 207 +++++------------- asio/include/asio/impl/use_awaitable.hpp | 173 +++++---------- asio/include/asio/impl/use_future.hpp | 150 ++++--------- asio/src/Makefile.mgw | 1 + asio/src/Makefile.msc | 1 + asio/src/tests/Makefile.am | 3 + asio/src/tests/unit/.gitignore | 1 + asio/src/tests/unit/disposition.cpp | 118 ++++++++++ 12 files changed, 539 insertions(+), 384 deletions(-) create mode 100644 asio/include/asio/disposition.hpp create mode 100644 asio/src/tests/unit/disposition.cpp diff --git a/asio/include/Makefile.am b/asio/include/Makefile.am index 3491ceef2d..574c81dc2c 100644 --- a/asio/include/Makefile.am +++ b/asio/include/Makefile.am @@ -347,6 +347,7 @@ nobase_include_HEADERS = \ asio/detail/work_dispatcher.hpp \ asio/detail/wrapped_handler.hpp \ asio/dispatch.hpp \ + asio/disposition.hpp \ asio/error_code.hpp \ asio/error.hpp \ asio/execution.hpp \ diff --git a/asio/include/asio.hpp b/asio/include/asio.hpp index d103d18a62..fbe91d0cc8 100644 --- a/asio/include/asio.hpp +++ b/asio/include/asio.hpp @@ -79,6 +79,7 @@ #include "asio/default_completion_token.hpp" #include "asio/detached.hpp" #include "asio/dispatch.hpp" +#include "asio/disposition.hpp" #include "asio/error.hpp" #include "asio/error_code.hpp" #include "asio/execution.hpp" diff --git a/asio/include/asio/detail/type_traits.hpp b/asio/include/asio/detail/type_traits.hpp index a4706a5ff1..6a8421be43 100644 --- a/asio/include/asio/detail/type_traits.hpp +++ b/asio/include/asio/detail/type_traits.hpp @@ -77,6 +77,8 @@ using std::is_convertible; using std::is_copy_constructible; +using std::is_nothrow_default_constructible; + using std::is_destructible; using std::is_function; @@ -85,6 +87,8 @@ using std::is_move_constructible; using std::is_nothrow_copy_constructible; +using std::is_nothrow_copy_assignable; + using std::is_nothrow_destructible; using std::is_object; diff --git a/asio/include/asio/disposition.hpp b/asio/include/asio/disposition.hpp new file mode 100644 index 0000000000..bed2f1fbc0 --- /dev/null +++ b/asio/include/asio/disposition.hpp @@ -0,0 +1,263 @@ +// +// disposition.hpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// 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) +// + +#ifndef ASIO_DISPOSITION_HPP +#define ASIO_DISPOSITION_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/throw_exception.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/error_code.hpp" +#include "asio/system_error.hpp" +#include + +#include "asio/detail/push_options.hpp" + +namespace asio { + +/// Traits type to adapt arbtirary error types as dispositions. +/** + * This type may be specialised for user-defined types, to allow them to be + * treated as a disposition by asio. + * + * The primary trait is not defined. + */ +#if defined(GENERATING_DOCUMENTATION) +template +struct disposition_traits +{ + /// Determine whether a disposition represents no error. + static bool not_an_error(const T& d) noexcept; + + /// Throw an exception if the disposition represents an error. + static void throw_exception(T d); + + /// Convert a disposition into an @c exception_ptr. + static std::exception_ptr to_exception_ptr(T d) noexcept; +}; +#else // defined(GENERATING_DOCUMENTATION) +template +struct disposition_traits; +#endif // defined(GENERATING_DOCUMENTATION) + +namespace detail { + +template +struct is_disposition_impl : false_type +{ +}; + +template +struct is_disposition_impl::value + >, + enable_if_t< + is_nothrow_copy_constructible::value + >, + enable_if_t< + is_nothrow_copy_assignable::value + >, + enable_if_t< + is_same< + decltype(disposition_traits::not_an_error(declval())), + bool + >::value + >, + void_t< + decltype(disposition_traits::throw_exception(declval())) + >, + enable_if_t< + is_same< + decltype(disposition_traits::to_exception_ptr(declval())), + std::exception_ptr + >::value + >> : true_type +{ +}; + +} // namespace detail + +/// Trait used for testing whether a type satisfies the requirements of a +/// disposition. +/** + * To be a valid disposition, a type must be nothrow default-constructible, + * nothrow copy-constructible, nothrow copy-assignable, and there must be a + * specialisation of the disposition_traits template for the type that provides + * the following static member functions: + * @li @c not_an_error: Takes an argument of type const T& and returns + * a @c bool. + * @li @c throw_exception: Takes an argument of type T. The + * caller of this function must not pass a disposition value for which + * @c not_an_error returns true. This function must not return. + * @li @c to_exception_ptr: Takes an argument of type T and returns a + * value of type @c std::exception_ptr. + */ +template +struct is_disposition : +#if defined(GENERATING_DOCUMENTATION) + integral_constant +#else // defined(GENERATING_DOCUMENTATION) + detail::is_disposition_impl +#endif // defined(GENERATING_DOCUMENTATION) +{ +}; + +#if defined(ASIO_HAS_VARIABLE_TEMPLATES) + +template +constexpr const bool is_disposition_v = is_disposition::value; + +#endif // defined(ASIO_HAS_VARIABLE_TEMPLATES) + +#if defined(ASIO_HAS_CONCEPTS) + +template +ASIO_CONCEPT disposition = is_disposition::value; + +#define ASIO_DISPOSITION ::asio::disposition + +#else // defined(ASIO_HAS_CONCEPTS) + +#define ASIO_DISPOSITION typename + +#endif // defined(ASIO_HAS_CONCEPTS) + +/// Specialisation of @c disposition_traits for @c error_code. +template <> +struct disposition_traits +{ + static bool not_an_error(const asio::error_code& ec) noexcept + { + return !ec; + } + + static void throw_exception(const asio::error_code& ec) + { + detail::throw_exception(asio::system_error(ec)); + } + + static std::exception_ptr to_exception_ptr( + const asio::error_code& ec) noexcept + { + return ec + ? std::make_exception_ptr(asio::system_error(ec)) + : nullptr; + } +}; + +/// Specialisation of @c disposition_traits for @c std::exception_ptr. +template <> +struct disposition_traits +{ + static bool not_an_error(const std::exception_ptr& e) noexcept + { + return !e; + } + + static void throw_exception(std::exception_ptr e) + { + std::rethrow_exception(static_cast(e)); + } + + static std::exception_ptr to_exception_ptr(std::exception_ptr e) noexcept + { + return e; + } +}; + +/// A tag type used to indicate the absence of an error. +struct no_error_t +{ + /// Default constructor. + constexpr no_error_t() + { + } + + /// Equality operator. + friend constexpr bool operator==( + const no_error_t&, const no_error_t&) noexcept + { + return true; + } + + /// Inequality operator. + friend constexpr bool operator!=( + const no_error_t&, const no_error_t&) noexcept + { + return false; + } + + /// Equality operator, returns true if the disposition does not contain an + /// error. + template + friend constexpr constraint_t::value, bool> + operator==(const no_error_t&, const Disposition& d) noexcept + { + return disposition_traits::not_an_error(d); + } + + /// Equality operator, returns true if the disposition does not contain an + /// error. + template + friend constexpr constraint_t::value, bool> + operator==(const Disposition& d, const no_error_t&) noexcept + { + return disposition_traits::not_an_error(d); + } + + /// Inequality operator, returns true if the disposition contains an error. + template + friend constexpr constraint_t::value, bool> + operator!=(const no_error_t&, const Disposition& d) noexcept + { + return !disposition_traits::not_an_error(d); + } + + /// Inequality operator, returns true if the disposition contains an error. + template + friend constexpr constraint_t::value, bool> + operator!=(const Disposition& d, const no_error_t&) noexcept + { + return !disposition_traits::not_an_error(d); + } +}; + +/// A special value used to indicate the absence of an error. +ASIO_INLINE_VARIABLE constexpr no_error_t no_error; + +/// Helper function to throw an exception arising from a disposition. +template +inline void throw_exception(Disposition&& d, + constraint_t>::value> = 0) +{ + disposition_traits>::throw_exception( + static_cast(d)); +} + +/// Helper function to convert a disposition to an @c exception_ptr. +template +inline std::exception_ptr to_exception_ptr(Disposition&& d, + constraint_t>::value> = 0) noexcept +{ + return disposition_traits>::to_exception_ptr( + static_cast(d)); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DISPOSITION_HPP diff --git a/asio/include/asio/impl/awaitable.hpp b/asio/include/asio/impl/awaitable.hpp index b32ae9c937..4be6db745d 100644 --- a/asio/include/asio/impl/awaitable.hpp +++ b/asio/include/asio/impl/awaitable.hpp @@ -21,10 +21,12 @@ #include #include "asio/cancellation_signal.hpp" #include "asio/cancellation_state.hpp" +#include "asio/detail/memory.hpp" #include "asio/detail/thread_context.hpp" #include "asio/detail/thread_info_base.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/type_traits.hpp" +#include "asio/disposition.hpp" #include "asio/error.hpp" #include "asio/post.hpp" #include "asio/system_error.hpp" @@ -42,7 +44,7 @@ namespace asio { namespace detail { struct awaitable_thread_has_context_switched {}; -template class awaitable_async_op_handler; +template class awaitable_async_op_handler; template class awaitable_async_op; // An awaitable_thread represents a thread-of-execution that is composed of one @@ -135,19 +137,15 @@ class awaitable_frame_base return result{this}; } - void set_except(std::exception_ptr e) noexcept + template + void set_disposition(Disposition&& d) noexcept { - pending_exception_ = e; - } - - void set_error(const asio::error_code& ec) - { - this->set_except(std::make_exception_ptr(asio::system_error(ec))); + pending_exception_ = (to_exception_ptr)(static_cast(d)); } void unhandled_exception() { - set_except(std::current_exception()); + set_disposition(std::current_exception()); } void rethrow_exception() @@ -634,7 +632,8 @@ class awaitable_frame private: template friend class awaitable_frame_base; - template friend class awaitable_async_op_handler; + template + friend class awaitable_async_op_handler; template friend class awaitable_handler_base; template friend class awaitable_thread; @@ -780,7 +779,7 @@ class awaitable_thread awaitable bottom_of_stack_; }; -template +template class awaitable_async_op_handler; template @@ -808,74 +807,9 @@ class awaitable_async_op_handler } }; -template -class awaitable_async_op_handler - : public awaitable_thread -{ -public: - typedef asio::error_code* result_type; - - awaitable_async_op_handler( - awaitable_thread* h, result_type& result) - : awaitable_thread(std::move(*h)), - result_(result) - { - } - - void operator()(asio::error_code ec) - { - result_ = &ec; - this->entry_point()->top_of_stack_->attach_thread(this); - this->entry_point()->top_of_stack_->clear_cancellation_slot(); - this->pump(); - } - - static void resume(result_type& result) - { - throw_error(*result); - } - -private: - result_type& result_; -}; - -template -class awaitable_async_op_handler - : public awaitable_thread -{ -public: - typedef std::exception_ptr* result_type; - - awaitable_async_op_handler( - awaitable_thread* h, result_type& result) - : awaitable_thread(std::move(*h)), - result_(result) - { - } - - void operator()(std::exception_ptr ex) - { - result_ = &ex; - this->entry_point()->top_of_stack_->attach_thread(this); - this->entry_point()->top_of_stack_->clear_cancellation_slot(); - this->pump(); - } - - static void resume(result_type& result) - { - if (*result) - { - std::exception_ptr ex = std::exchange(*result, nullptr); - std::rethrow_exception(ex); - } - } - -private: - result_type& result_; -}; - template -class awaitable_async_op_handler +class awaitable_async_op_handler::value>> : public awaitable_thread { public: @@ -890,7 +824,7 @@ class awaitable_async_op_handler void operator()(T result) { - result_ = &result; + result_ = detail::addressof(result); this->entry_point()->top_of_stack_->attach_thread(this); this->entry_point()->top_of_stack_->clear_cancellation_slot(); this->pump(); @@ -905,16 +839,13 @@ class awaitable_async_op_handler result_type& result_; }; -template -class awaitable_async_op_handler +template +class awaitable_async_op_handler::value>> : public awaitable_thread { public: - struct result_type - { - asio::error_code* ec_; - T* value_; - }; + typedef Disposition* result_type; awaitable_async_op_handler( awaitable_thread* h, result_type& result) @@ -923,33 +854,36 @@ class awaitable_async_op_handler { } - void operator()(asio::error_code ec, T value) + void operator()(Disposition d) { - result_.ec_ = &ec; - result_.value_ = &value; + result_ = detail::addressof(d); this->entry_point()->top_of_stack_->attach_thread(this); this->entry_point()->top_of_stack_->clear_cancellation_slot(); this->pump(); } - static T resume(result_type& result) + static void resume(result_type& result) { - throw_error(*result.ec_); - return std::move(*result.value_); + if (*result != no_error) + { + Disposition d = std::exchange(*result, Disposition()); + asio::throw_exception(static_cast(d)); + } } private: result_type& result_; }; -template -class awaitable_async_op_handler +template +class awaitable_async_op_handler::value>> : public awaitable_thread { public: struct result_type { - std::exception_ptr* ex_; + Disposition* disposition_; T* value_; }; @@ -960,10 +894,10 @@ class awaitable_async_op_handler { } - void operator()(std::exception_ptr ex, T value) + void operator()(Disposition d, T value) { - result_.ex_ = &ex; - result_.value_ = &value; + result_.disposition_ = detail::addressof(d); + result_.value_ = detail::addressof(value); this->entry_point()->top_of_stack_->attach_thread(this); this->entry_point()->top_of_stack_->clear_cancellation_slot(); this->pump(); @@ -971,10 +905,10 @@ class awaitable_async_op_handler static T resume(result_type& result) { - if (*result.ex_) + if (*result.disposition_ != no_error) { - std::exception_ptr ex = std::exchange(*result.ex_, nullptr); - std::rethrow_exception(ex); + Disposition d = std::exchange(*result.disposition_, Disposition()); + asio::throw_exception(d); } return std::move(*result.value_); } @@ -983,12 +917,13 @@ class awaitable_async_op_handler result_type& result_; }; -template -class awaitable_async_op_handler +template +class awaitable_async_op_handler::value>> : public awaitable_thread { public: - typedef std::tuple* result_type; + typedef std::tuple* result_type; awaitable_async_op_handler( awaitable_thread* h, result_type& result) @@ -1000,14 +935,14 @@ class awaitable_async_op_handler template void operator()(Args&&... args) { - std::tuple result(std::forward(args)...); - result_ = &result; + std::tuple result(std::forward(args)...); + result_ = detail::addressof(result); this->entry_point()->top_of_stack_->attach_thread(this); this->entry_point()->top_of_stack_->clear_cancellation_slot(); this->pump(); } - static std::tuple resume(result_type& result) + static std::tuple resume(result_type& result) { return std::move(*result); } @@ -1016,53 +951,15 @@ class awaitable_async_op_handler result_type& result_; }; -template -class awaitable_async_op_handler - : public awaitable_thread -{ -public: - struct result_type - { - asio::error_code* ec_; - std::tuple* value_; - }; - - awaitable_async_op_handler( - awaitable_thread* h, result_type& result) - : awaitable_thread(std::move(*h)), - result_(result) - { - } - - template - void operator()(asio::error_code ec, Args&&... args) - { - result_.ec_ = &ec; - std::tuple value(std::forward(args)...); - result_.value_ = &value; - this->entry_point()->top_of_stack_->attach_thread(this); - this->entry_point()->top_of_stack_->clear_cancellation_slot(); - this->pump(); - } - - static std::tuple resume(result_type& result) - { - throw_error(*result.ec_); - return std::move(*result.value_); - } - -private: - result_type& result_; -}; - -template -class awaitable_async_op_handler +template +class awaitable_async_op_handler::value>> : public awaitable_thread { public: struct result_type { - std::exception_ptr* ex_; + Disposition* disposition_; std::tuple* value_; }; @@ -1074,11 +971,11 @@ class awaitable_async_op_handler } template - void operator()(std::exception_ptr ex, Args&&... args) + void operator()(Disposition d, Args&&... args) { - result_.ex_ = &ex; + result_.disposition_ = detail::addressof(d); std::tuple value(std::forward(args)...); - result_.value_ = &value; + result_.value_ = detail::addressof(value); this->entry_point()->top_of_stack_->attach_thread(this); this->entry_point()->top_of_stack_->clear_cancellation_slot(); this->pump(); @@ -1086,10 +983,10 @@ class awaitable_async_op_handler static std::tuple resume(result_type& result) { - if (*result.ex_) + if (*result.disposition_ != no_error) { - std::exception_ptr ex = std::exchange(*result.ex_, nullptr); - std::rethrow_exception(ex); + Disposition d = std::exchange(*result.disposition_, Disposition()); + asio::throw_exception(d); } return std::move(*result.value_); } diff --git a/asio/include/asio/impl/use_awaitable.hpp b/asio/include/asio/impl/use_awaitable.hpp index 3cd95165c4..d426f6c563 100644 --- a/asio/include/asio/impl/use_awaitable.hpp +++ b/asio/include/asio/impl/use_awaitable.hpp @@ -18,6 +18,7 @@ #include "asio/detail/config.hpp" #include "asio/async_result.hpp" #include "asio/cancellation_signal.hpp" +#include "asio/disposition.hpp" #include "asio/detail/push_options.hpp" @@ -73,77 +74,27 @@ class awaitable_handler } }; -template -class awaitable_handler - : public awaitable_handler_base -{ -public: - using awaitable_handler_base::awaitable_handler_base; - - void operator()(const asio::error_code& ec) - { - this->frame()->attach_thread(this); - if (ec) - this->frame()->set_error(ec); - else - this->frame()->return_void(); - this->frame()->clear_cancellation_slot(); - this->frame()->pop_frame(); - this->pump(); - } -}; - -template -class awaitable_handler - : public awaitable_handler_base -{ -public: - using awaitable_handler_base::awaitable_handler_base; - - void operator()(std::exception_ptr ex) - { - this->frame()->attach_thread(this); - if (ex) - this->frame()->set_except(ex); - else - this->frame()->return_void(); - this->frame()->clear_cancellation_slot(); - this->frame()->pop_frame(); - this->pump(); - } -}; - template class awaitable_handler - : public awaitable_handler_base + : public awaitable_handler_base::value, void, T>> { public: - using awaitable_handler_base::awaitable_handler_base; + using awaitable_handler_base::value, void, T>> + ::awaitable_handler_base; template void operator()(Arg&& arg) { this->frame()->attach_thread(this); - this->frame()->return_value(std::forward(arg)); - this->frame()->clear_cancellation_slot(); - this->frame()->pop_frame(); - this->pump(); - } -}; - -template -class awaitable_handler - : public awaitable_handler_base -{ -public: - using awaitable_handler_base::awaitable_handler_base; - - template - void operator()(const asio::error_code& ec, Arg&& arg) - { - this->frame()->attach_thread(this); - if (ec) - this->frame()->set_error(ec); + if constexpr (is_disposition::value) + { + if (arg == no_error) + this->frame()->return_void(); + else + this->frame()->set_disposition(arg); + } else this->frame()->return_value(std::forward(arg)); this->frame()->clear_cancellation_slot(); @@ -152,84 +103,66 @@ class awaitable_handler } }; -template -class awaitable_handler - : public awaitable_handler_base -{ -public: - using awaitable_handler_base::awaitable_handler_base; - - template - void operator()(std::exception_ptr ex, Arg&& arg) - { - this->frame()->attach_thread(this); - if (ex) - this->frame()->set_except(ex); - else - this->frame()->return_value(std::forward(arg)); - this->frame()->clear_cancellation_slot(); - this->frame()->pop_frame(); - this->pump(); - } -}; - -template -class awaitable_handler - : public awaitable_handler_base> -{ -public: - using awaitable_handler_base>::awaitable_handler_base; - - template - void operator()(Args&&... args) - { - this->frame()->attach_thread(this); - this->frame()->return_values(std::forward(args)...); - this->frame()->clear_cancellation_slot(); - this->frame()->pop_frame(); - this->pump(); - } -}; - -template -class awaitable_handler - : public awaitable_handler_base> +template +class awaitable_handler + : public awaitable_handler_base::value, T1, std::tuple>> { public: using awaitable_handler_base>::awaitable_handler_base; + conditional_t::value, T1, std::tuple>> + ::awaitable_handler_base; - template - void operator()(const asio::error_code& ec, Args&&... args) + template + void operator()(Arg0&& arg0, Arg1&& arg1) { this->frame()->attach_thread(this); - if (ec) - this->frame()->set_error(ec); + if constexpr (is_disposition::value) + { + if (arg0 == no_error) + this->frame()->return_value(std::forward(arg1)); + else + this->frame()->set_disposition(std::forward(arg0)); + } else - this->frame()->return_values(std::forward(args)...); + { + this->frame()->return_values(std::forward(arg0), + std::forward(arg1)); + } this->frame()->clear_cancellation_slot(); this->frame()->pop_frame(); this->pump(); } }; -template -class awaitable_handler - : public awaitable_handler_base> +template +class awaitable_handler + : public awaitable_handler_base::value, + std::tuple, std::tuple>> { public: using awaitable_handler_base>::awaitable_handler_base; + conditional_t::value, + std::tuple, std::tuple>> + ::awaitable_handler_base; - template - void operator()(std::exception_ptr ex, Args&&... args) + template + void operator()(Arg0&& arg0, Args&&... args) { this->frame()->attach_thread(this); - if (ex) - this->frame()->set_except(ex); + if constexpr (is_disposition::value) + { + if (arg0 == no_error) + this->frame()->return_values(std::forward(args)...); + else + this->frame()->set_disposition(std::forward(arg0)); + } else - this->frame()->return_values(std::forward(args)...); + { + this->frame()->return_values(std::forward(arg0), + std::forward(args)...); + } this->frame()->clear_cancellation_slot(); this->frame()->pop_frame(); this->pump(); diff --git a/asio/include/asio/impl/use_future.hpp b/asio/include/asio/impl/use_future.hpp index 579b93f5f3..7ba3602d76 100644 --- a/asio/include/asio/impl/use_future.hpp +++ b/asio/include/asio/impl/use_future.hpp @@ -19,8 +19,9 @@ #include #include "asio/async_result.hpp" #include "asio/detail/memory.hpp" +#include "asio/detail/type_traits.hpp" #include "asio/dispatch.hpp" -#include "asio/error_code.hpp" +#include "asio/disposition.hpp" #include "asio/execution.hpp" #include "asio/packaged_task.hpp" #include "asio/system_error.hpp" @@ -228,36 +229,17 @@ class promise_handler_0 } }; -// For completion signature void(error_code). -class promise_handler_ec_0 +// For completion signature void(disposition auto). +template +class promise_handler_d_0 : public promise_creator { public: - void operator()(const asio::error_code& ec) + void operator()(const Disposition& d) { - if (ec) + if (d != no_error) { - this->p_->set_exception( - std::make_exception_ptr( - asio::system_error(ec))); - } - else - { - this->p_->set_value(); - } - } -}; - -// For completion signature void(exception_ptr). -class promise_handler_ex_0 - : public promise_creator -{ -public: - void operator()(const std::exception_ptr& ex) - { - if (ex) - { - this->p_->set_exception(ex); + this->p_->set_exception((to_exception_ptr)(d)); } else { @@ -279,44 +261,24 @@ class promise_handler_1 } }; -// For completion signature void(error_code, T). -template -class promise_handler_ec_1 +// For completion signature void(disposition auto, T). +template +class promise_handler_d_1 : public promise_creator { public: template - void operator()(const asio::error_code& ec, - Arg&& arg) + void operator()(const Disposition& d, Arg&& arg) { - if (ec) + if (d != no_error) { - this->p_->set_exception( - std::make_exception_ptr( - asio::system_error(ec))); + this->p_->set_exception((to_exception_ptr)(d)); } else this->p_->set_value(static_cast(arg)); } }; -// For completion signature void(exception_ptr, T). -template -class promise_handler_ex_1 - : public promise_creator -{ -public: - template - void operator()(const std::exception_ptr& ex, - Arg&& arg) - { - if (ex) - this->p_->set_exception(ex); - else - this->p_->set_value(static_cast(arg)); - } -}; - // For completion signature void(T1, ..., Tn); template class promise_handler_n @@ -333,19 +295,17 @@ class promise_handler_n }; // For completion signature void(error_code, T1, ..., Tn); -template -class promise_handler_ec_n +template +class promise_handler_d_n : public promise_creator { public: template - void operator()(const asio::error_code& ec, Args&&... args) + void operator()(const Disposition& d, Args&&... args) { - if (ec) + if (d != no_error) { - this->p_->set_exception( - std::make_exception_ptr( - asio::system_error(ec))); + this->p_->set_exception((to_exception_ptr)(d)); } else { @@ -356,66 +316,38 @@ class promise_handler_ec_n } }; -// For completion signature void(exception_ptr, T1, ..., Tn); -template -class promise_handler_ex_n - : public promise_creator -{ -public: - template - void operator()(const std::exception_ptr& ex, - Args&&... args) - { - if (ex) - this->p_->set_exception(ex); - else - { - this->p_->set_value( - std::forward_as_tuple( - static_cast(args)...)); - } - } -}; - // Helper template to choose the appropriate concrete promise handler // implementation based on the supplied completion signature. -template class promise_handler_selector; +template class promise_handler_selector; template <> class promise_handler_selector : public promise_handler_0 {}; -template <> -class promise_handler_selector - : public promise_handler_ec_0 {}; - -template <> -class promise_handler_selector - : public promise_handler_ex_0 {}; - -template -class promise_handler_selector - : public promise_handler_1 {}; - template -class promise_handler_selector - : public promise_handler_ec_1 {}; +class promise_handler_selector::value>> + : public promise_handler_d_0 {}; template -class promise_handler_selector - : public promise_handler_ex_1 {}; - -template -class promise_handler_selector - : public promise_handler_n> {}; - -template -class promise_handler_selector - : public promise_handler_ec_n> {}; - -template -class promise_handler_selector - : public promise_handler_ex_n> {}; +class promise_handler_selector::value>> + : public promise_handler_1 {}; + +template +class promise_handler_selector::value>> + : public promise_handler_d_1 {}; + +template +class promise_handler_selector::value>> + : public promise_handler_n> {}; + +template +class promise_handler_selector::value>> + : public promise_handler_d_n> {}; // Completion handlers produced from the use_future completion token, when not // using use_future::operator(). diff --git a/asio/src/Makefile.mgw b/asio/src/Makefile.mgw index 05d9e8420c..f2cbaa7be8 100644 --- a/asio/src/Makefile.mgw +++ b/asio/src/Makefile.mgw @@ -76,6 +76,7 @@ UNIT_TEST_EXES = \ tests/unit/defer.exe \ tests/unit/detached.exe \ tests/unit/dispatch.exe \ + tests/unit/disposition.exe \ tests/unit/error.exe \ tests/unit/execution_context.exe \ tests/unit/execution/any_executor.exe \ diff --git a/asio/src/Makefile.msc b/asio/src/Makefile.msc index 5e18fa4cec..fc1e22c42c 100644 --- a/asio/src/Makefile.msc +++ b/asio/src/Makefile.msc @@ -169,6 +169,7 @@ UNIT_TEST_EXES = \ tests\unit\deferred.exe \ tests\unit\detached.exe \ tests\unit\dispatch.exe \ + tests\unit\disposition.exe \ tests\unit\error.exe \ tests\unit\execution_context.exe \ tests\unit\execution\any_executor.exe \ diff --git a/asio/src/tests/Makefile.am b/asio/src/tests/Makefile.am index fab4af33ea..c36205ca7d 100644 --- a/asio/src/tests/Makefile.am +++ b/asio/src/tests/Makefile.am @@ -69,6 +69,7 @@ check_PROGRAMS = \ unit/deferred \ unit/detached \ unit/dispatch \ + unit/disposition \ unit/error \ unit/execution/any_executor \ unit/execution/blocking \ @@ -288,6 +289,7 @@ TESTS = \ unit/deferred \ unit/detached \ unit/dispatch \ + unit/disposition \ unit/error \ unit/execution/any_executor \ unit/execution/blocking \ @@ -507,6 +509,7 @@ unit_defer_SOURCES = unit/defer.cpp unit_deferred_SOURCES = unit/deferred.cpp unit_detached_SOURCES = unit/detached.cpp unit_dispatch_SOURCES = unit/dispatch.cpp +unit_disposition_SOURCES = unit/disposition.cpp unit_error_SOURCES = unit/error.cpp unit_execution_any_executor_SOURCES = unit/execution/any_executor.cpp unit_execution_blocking_SOURCES = unit/execution/blocking.cpp diff --git a/asio/src/tests/unit/.gitignore b/asio/src/tests/unit/.gitignore index 8c8bebfd7d..07f2742f16 100644 --- a/asio/src/tests/unit/.gitignore +++ b/asio/src/tests/unit/.gitignore @@ -64,6 +64,7 @@ defer deferred detached dispatch +disposition error error_handler execution_context diff --git a/asio/src/tests/unit/disposition.cpp b/asio/src/tests/unit/disposition.cpp new file mode 100644 index 0000000000..4d824f50da --- /dev/null +++ b/asio/src/tests/unit/disposition.cpp @@ -0,0 +1,118 @@ +// +// disposition.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// 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) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include "asio/disposition.hpp" + +#include "asio/error.hpp" + +#include "unit_test.hpp" + +void no_error_test() +{ + using asio::no_error; + + ASIO_CHECK(no_error == no_error); + ASIO_CHECK(!(no_error != no_error)); +} + +void error_code_disposition_test() +{ + using asio::no_error; + + asio::error_code ec1; + + ASIO_CHECK(ec1 == no_error); + ASIO_CHECK(no_error == ec1); + ASIO_CHECK(!(ec1 != no_error)); + ASIO_CHECK(!(no_error != ec1)); + + std::exception_ptr ep1 = asio::to_exception_ptr(ec1); + ASIO_CHECK(ep1 == nullptr); + + asio::error_code ec2 = asio::error::eof; + + ASIO_CHECK(!(ec2 == no_error)); + ASIO_CHECK(!(no_error == ec2)); + ASIO_CHECK(ec2 != no_error); + ASIO_CHECK(no_error != ec2); + +#if !defined(ASIO_NO_EXCEPTIONS) + bool caught; + try + { + asio::throw_exception(ec2); + caught = false; + } + catch (const asio::system_error& ex) + { + caught = true; + ASIO_CHECK(ex.code() == asio::error::eof); + } + ASIO_CHECK(caught); +#endif // !defined(ASIO_NO_EXCEPTIONS) + + std::exception_ptr ep2 = asio::to_exception_ptr(ec2); + ASIO_CHECK(ep2 != nullptr); +} + +void exception_ptr_disposition_test() +{ + using asio::no_error; + + std::exception_ptr ep1; + + ASIO_CHECK(ep1 == no_error); + ASIO_CHECK(no_error == ep1); + ASIO_CHECK(!(ep1 != no_error)); + ASIO_CHECK(!(no_error != ep1)); + + std::exception_ptr ep2 = asio::to_exception_ptr(ep1); + ASIO_CHECK(ep1 == nullptr); + + std::exception_ptr ep3 = std::make_exception_ptr( + asio::system_error(asio::error::eof)); + + ASIO_CHECK(!(ep3 == no_error)); + ASIO_CHECK(!(no_error == ep3)); + ASIO_CHECK(ep3 != no_error); + ASIO_CHECK(no_error != ep3); + +#if !defined(ASIO_NO_EXCEPTIONS) + bool caught; + try + { + asio::throw_exception(ep3); + caught = false; + } + catch (const asio::system_error& ex) + { + caught = true; + ASIO_CHECK(ex.code() == asio::error::eof); + } + ASIO_CHECK(caught); +#endif // !defined(ASIO_NO_EXCEPTIONS) + + std::exception_ptr ep4 = asio::to_exception_ptr(ep3); + ASIO_CHECK(ep4 != nullptr); +} + +ASIO_TEST_SUITE +( + "disposition", + ASIO_TEST_CASE(no_error_test) + ASIO_TEST_CASE(error_code_disposition_test) + ASIO_TEST_CASE(exception_ptr_disposition_test) +)