From 99bb2c6698e4819205eaeddfc0403a6b6771b28b Mon Sep 17 00:00:00 2001 From: Shauren Date: Mon, 14 Apr 2025 19:15:41 +0200 Subject: Core/Utilities: Added boost asio callback that transforms callback signatures from (error_code + other) to (boost::outcome) --- src/common/Asio/ExpectedCompletionHandler.h | 315 ++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 src/common/Asio/ExpectedCompletionHandler.h (limited to 'src') diff --git a/src/common/Asio/ExpectedCompletionHandler.h b/src/common/Asio/ExpectedCompletionHandler.h new file mode 100644 index 00000000000..6379b9682e8 --- /dev/null +++ b/src/common/Asio/ExpectedCompletionHandler.h @@ -0,0 +1,315 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TRINITYCORE_EXPECTED_COMPLETION_HANDLER_H +#define TRINITYCORE_EXPECTED_COMPLETION_HANDLER_H + +#include +#include +#include +#include +#include +#include + +namespace Trinity::Asio +{ +/// The AsExpected class is used to indicate that any arguments to the +/// completion handler should be combined and passed as a single @c boost::outcome_v2::result argument. +/// The arguments are first moved into a @c boost::outcome_v2::result and that is then +/// passed to the completion handler. +template +class AsExpected +{ +public: + // First dummy argument is used to prevent the "default" constructor from being used for implicit conversions + constexpr AsExpected(std::type_identity = {}, CompletionToken token = {}) + : token_(std::forward(token)) + { + } + + template requires (!std::same_as) + constexpr explicit AsExpected(T&& completion_token) + : token_(std::forward(completion_token)) + { + } + + /// Adapts an executor to add the @c AsExpected completion token as the + /// default. + template + struct executor_with_default : InnerExecutor + { + /// Specify @c AsExpected as the default completion token type. + typedef AsExpected default_completion_token_type; + + /// Construct the adapted executor from the inner executor type. + template + executor_with_default(InnerExecutor1 const& ex, + std::enable_if_t< + std::conditional_t< + !std::is_same_v, + std::is_convertible, + std::false_type + >::value, + int + > = 0) noexcept + : InnerExecutor(ex) + { + } + }; + + /// Type alias to adapt an I/O object to use @c AsExpected as its + /// default completion token type. + template + using as_default_on_t = typename T::template rebind_executor>::other; + + /// Function helper to adapt an I/O object to use @c AsExpected as its + /// default completion token type. + template + static auto as_default_on(T&& object) + { + return as_default_on_t>(static_cast(object)); + } + + CompletionToken token_; +}; + +struct AsExpectedFn +{ + /// Adapt a @ref completion_token to specify that the completion handler + /// arguments should be combined into a single @c boost::outcome_v2::result argument. + template + [[nodiscard]] inline constexpr AsExpected> operator()(CompletionToken&& completion_token) const + { + return AsExpected>(static_cast(completion_token)); + } +}; + +/// A function object that adapts a @ref completion_token to specify that the +/// completion handler arguments should be combined into a single @c boost::outcome_v2::result +/// argument. +/// +/// May also be used directly as a completion token, in which case it adapts the +/// asynchronous operation's default completion token (or @ref boost::asio::deferred +/// if no default is available). +inline constexpr AsExpectedFn as_expected; + +namespace Impl +{ +template +concept CompletionTokenError = std::same_as, boost::system::error_code> + || std::same_as, std::exception_ptr>; + +template +class AsExpectedHandler +{ +public: + typedef void result_type; + + template + AsExpectedHandler(AsExpected e) : handler_(static_cast(e.token_)) { } + + template requires (!std::same_as) + AsExpectedHandler(RedirectedHandler&& h) : handler_(std::forward(h)) { } + + template + inline void operator()(Error&& e) + { + using return_type = boost::outcome_v2::result>; + + if (e) + static_cast(handler_)(return_type(boost::outcome_v2::failure(std::forward(e)))); + else + static_cast(handler_)(return_type(boost::outcome_v2::success())); + } + + template + inline void operator()(Error&& e, Arg&& value) + { + using return_type = boost::outcome_v2::result, std::remove_cvref_t>; + + if (e) + static_cast(handler_)(return_type(boost::outcome_v2::failure(std::forward(e)))); + else + static_cast(handler_)(return_type(boost::outcome_v2::success(std::forward(value)))); + } + + template + inline void operator()(Error&& e, Arg&& first, Args&&... rest) + { + using return_type = boost::outcome_v2::result, std::decay_t...>, std::remove_cvref_t>; + + if (e) + static_cast(handler_)(return_type(boost::outcome_v2::failure(std::forward(e)))); + else + static_cast(handler_)(return_type(boost::outcome_v2::success(std::make_tuple(std::forward(first), std::forward(rest)...)))); + } + + Handler handler_; +}; + +template +inline bool asio_handler_is_continuation(AsExpectedHandler* this_handler) +{ + return boost_asio_handler_cont_helpers::is_continuation(this_handler->handler_); +} + +template +struct AsExpectedSignature; + +#define STAMP_AS_EXPECTED_SIGNATURE(qualifier) \ + template \ + struct AsExpectedSignature \ + { \ + using type = R(boost::outcome_v2::result>) qualifier; \ + }; \ + template \ + struct AsExpectedSignature \ + { \ + using type = R(boost::outcome_v2::result, std::remove_cvref_t>) qualifier; \ + }; \ + template \ + struct AsExpectedSignature \ + { \ + using type = R(boost::outcome_v2::result, std::decay_t...>, std::remove_cvref_t>) qualifier; \ + }; + +STAMP_AS_EXPECTED_SIGNATURE(BOOST_PP_EMPTY()); +STAMP_AS_EXPECTED_SIGNATURE(&); +STAMP_AS_EXPECTED_SIGNATURE(&&); +STAMP_AS_EXPECTED_SIGNATURE(noexcept); +STAMP_AS_EXPECTED_SIGNATURE(& noexcept); +STAMP_AS_EXPECTED_SIGNATURE(&& noexcept); + +} // namespace Impl +} + +namespace boost::asio +{ +#if BOOST_VERSION >= 107700 +template +class async_result, Signatures...> : async_result::type...> +{ + template + struct init_wrapper + { + explicit init_wrapper(Initiation const& initiation) : initiation_(initiation) { } + explicit init_wrapper(Initiation&& initiation) : initiation_(std::move(initiation)) { } + + template + inline void operator()(Handler&& handler, Args&&... args) && + { + static_cast(initiation_)(Trinity::Asio::Impl::AsExpectedHandler>(std::forward(handler)), std::forward(args)...); + } + + template + inline void operator()(Handler&& handler, Args&&... args) const & + { + static_cast(initiation_)(Trinity::Asio::Impl::AsExpectedHandler>(std::forward(handler)), std::forward(args)...); + } + + Initiation initiation_; + }; + +public: + template + static inline auto initiate(Initiation&& initiation, RawCompletionToken&& token, Args&&... args) + { + return async_initiate< + conditional_t< + is_const>::value, + CompletionToken const, CompletionToken>, + typename Trinity::Asio::Impl::AsExpectedSignature::type...>( + init_wrapper>( + std::forward(initiation)), + token.token_, std::forward(args)...); + } +}; + +template