aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2025-04-14 19:15:41 +0200
committerOvahlord <dreadkiller@gmx.de>2025-04-14 22:25:32 +0200
commit810d8d0f30ad30176fb2fc0594072246d349243a (patch)
treec2c930f3dd37e4a6d0cd1d07c306c8f984ee04a8
parent86a7f9e2ff5de8ce4e7bd53326a526847ac32403 (diff)
Core/Utilities: Added boost asio callback that transforms callback signatures from (error_code + other) to (boost::outcome)
(cherry picked from commit 99bb2c6698e4819205eaeddfc0403a6b6771b28b)
-rw-r--r--src/common/Asio/ExpectedCompletionHandler.h315
1 files changed, 315 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITYCORE_EXPECTED_COMPLETION_HANDLER_H
+#define TRINITYCORE_EXPECTED_COMPLETION_HANDLER_H
+
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/detail/handler_cont_helpers.hpp>
+#include <boost/outcome/result.hpp>
+#include <boost/preprocessor/empty.hpp>
+#include <type_traits>
+
+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 <typename CompletionToken>
+class AsExpected
+{
+public:
+ // First dummy argument is used to prevent the "default" constructor from being used for implicit conversions
+ constexpr AsExpected(std::type_identity<void> = {}, CompletionToken token = {})
+ : token_(std::forward<CompletionToken>(token))
+ {
+ }
+
+ template <typename T> requires (!std::same_as<T, AsExpected>)
+ constexpr explicit AsExpected(T&& completion_token)
+ : token_(std::forward<T>(completion_token))
+ {
+ }
+
+ /// Adapts an executor to add the @c AsExpected completion token as the
+ /// default.
+ template <typename InnerExecutor>
+ 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 <typename InnerExecutor1>
+ executor_with_default(InnerExecutor1 const& ex,
+ std::enable_if_t<
+ std::conditional_t<
+ !std::is_same_v<InnerExecutor1, executor_with_default>,
+ std::is_convertible<InnerExecutor1, InnerExecutor>,
+ 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 <typename T>
+ using as_default_on_t = typename T::template rebind_executor<executor_with_default<typename T::executor_type>>::other;
+
+ /// Function helper to adapt an I/O object to use @c AsExpected as its
+ /// default completion token type.
+ template <typename T>
+ static auto as_default_on(T&& object)
+ {
+ return as_default_on_t<std::decay_t<T>>(static_cast<T&&>(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 <typename CompletionToken>
+ [[nodiscard]] inline constexpr AsExpected<std::decay_t<CompletionToken>> operator()(CompletionToken&& completion_token) const
+ {
+ return AsExpected<std::decay_t<CompletionToken>>(static_cast<CompletionToken&&>(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 <typename T>
+concept CompletionTokenError = std::same_as<std::remove_cvref_t<T>, boost::system::error_code>
+ || std::same_as<std::remove_cvref_t<T>, std::exception_ptr>;
+
+template <typename Handler>
+class AsExpectedHandler
+{
+public:
+ typedef void result_type;
+
+ template <typename CompletionToken>
+ AsExpectedHandler(AsExpected<CompletionToken> e) : handler_(static_cast<CompletionToken&&>(e.token_)) { }
+
+ template <typename RedirectedHandler> requires (!std::same_as<RedirectedHandler, AsExpectedHandler>)
+ AsExpectedHandler(RedirectedHandler&& h) : handler_(std::forward<RedirectedHandler>(h)) { }
+
+ template <CompletionTokenError Error>
+ inline void operator()(Error&& e)
+ {
+ using return_type = boost::outcome_v2::result<void, std::remove_cvref_t<Error>>;
+
+ if (e)
+ static_cast<Handler&&>(handler_)(return_type(boost::outcome_v2::failure(std::forward<Error>(e))));
+ else
+ static_cast<Handler&&>(handler_)(return_type(boost::outcome_v2::success()));
+ }
+
+ template <CompletionTokenError Error, typename Arg>
+ inline void operator()(Error&& e, Arg&& value)
+ {
+ using return_type = boost::outcome_v2::result<std::decay_t<Arg>, std::remove_cvref_t<Error>>;
+
+ if (e)
+ static_cast<Handler&&>(handler_)(return_type(boost::outcome_v2::failure(std::forward<Error>(e))));
+ else
+ static_cast<Handler&&>(handler_)(return_type(boost::outcome_v2::success(std::forward<Arg>(value))));
+ }
+
+ template <CompletionTokenError Error, typename Arg, typename... Args>
+ inline void operator()(Error&& e, Arg&& first, Args&&... rest)
+ {
+ using return_type = boost::outcome_v2::result<std::tuple<std::decay_t<Arg>, std::decay_t<Args>...>, std::remove_cvref_t<Error>>;
+
+ if (e)
+ static_cast<Handler&&>(handler_)(return_type(boost::outcome_v2::failure(std::forward<Error>(e))));
+ else
+ static_cast<Handler&&>(handler_)(return_type(boost::outcome_v2::success(std::make_tuple(std::forward<Arg>(first), std::forward<Args>(rest)...))));
+ }
+
+ Handler handler_;
+};
+
+template <typename Handler>
+inline bool asio_handler_is_continuation(AsExpectedHandler<Handler>* this_handler)
+{
+ return boost_asio_handler_cont_helpers::is_continuation(this_handler->handler_);
+}
+
+template <typename Signature>
+struct AsExpectedSignature;
+
+#define STAMP_AS_EXPECTED_SIGNATURE(qualifier) \
+ template <typename R, CompletionTokenError Error> \
+ struct AsExpectedSignature<R(Error) qualifier> \
+ { \
+ using type = R(boost::outcome_v2::result<void, std::remove_cvref_t<Error>>) qualifier; \
+ }; \
+ template <typename R, CompletionTokenError Error, typename Arg> \
+ struct AsExpectedSignature<R(Error, Arg) qualifier> \
+ { \
+ using type = R(boost::outcome_v2::result<std::decay_t<Arg>, std::remove_cvref_t<Error>>) qualifier; \
+ }; \
+ template <typename R, CompletionTokenError Error, typename Arg, typename... Args> \
+ struct AsExpectedSignature<R(Error, Arg, Args...) qualifier> \
+ { \
+ using type = R(boost::outcome_v2::result<std::tuple<std::decay_t<Arg>, std::decay_t<Args>...>, std::remove_cvref_t<Error>>) 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 <typename CompletionToken, typename... Signatures>
+class async_result<Trinity::Asio::AsExpected<CompletionToken>, Signatures...> : async_result<CompletionToken, typename Trinity::Asio::Impl::AsExpectedSignature<Signatures>::type...>
+{
+ template <typename Initiation>
+ struct init_wrapper
+ {
+ explicit init_wrapper(Initiation const& initiation) : initiation_(initiation) { }
+ explicit init_wrapper(Initiation&& initiation) : initiation_(std::move(initiation)) { }
+
+ template <typename Handler, typename... Args>
+ inline void operator()(Handler&& handler, Args&&... args) &&
+ {
+ static_cast<Initiation&&>(initiation_)(Trinity::Asio::Impl::AsExpectedHandler<std::decay_t<Handler>>(std::forward<Handler>(handler)), std::forward<Args>(args)...);
+ }
+
+ template <typename Handler, typename... Args>
+ inline void operator()(Handler&& handler, Args&&... args) const &
+ {
+ static_cast<Initiation const&>(initiation_)(Trinity::Asio::Impl::AsExpectedHandler<std::decay_t<Handler>>(std::forward<Handler>(handler)), std::forward<Args>(args)...);
+ }
+
+ Initiation initiation_;
+ };
+
+public:
+ template <typename Initiation, typename RawCompletionToken, typename... Args>
+ static inline auto initiate(Initiation&& initiation, RawCompletionToken&& token, Args&&... args)
+ {
+ return async_initiate<
+ conditional_t<
+ is_const<remove_reference_t<RawCompletionToken>>::value,
+ CompletionToken const, CompletionToken>,
+ typename Trinity::Asio::Impl::AsExpectedSignature<Signatures>::type...>(
+ init_wrapper<std::decay_t<Initiation>>(
+ std::forward<Initiation>(initiation)),
+ token.token_, std::forward<Args>(args)...);
+ }
+};
+
+template <template <typename, typename> class Associator, typename Handler, typename DefaultCandidate, typename _>
+struct associator;
+
+template <template <typename, typename> class Associator, typename Handler, typename DefaultCandidate>
+struct associator<Associator, Trinity::Asio::Impl::AsExpectedHandler<Handler>, DefaultCandidate, void> : Associator<Handler, DefaultCandidate>
+{
+ static inline auto get(Trinity::Asio::Impl::AsExpectedHandler<Handler> const& h) noexcept
+ {
+ return Associator<Handler, DefaultCandidate>::get(h.handler_);
+ }
+
+ static inline auto get(Trinity::Asio::Impl::AsExpectedHandler<Handler> const& h, DefaultCandidate const& c) noexcept
+ {
+ return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
+ }
+};
+
+template <typename... Signatures>
+class async_result<Trinity::Asio::AsExpectedFn, Signatures...>
+{
+public:
+ template <typename Initiation, typename RawCompletionToken, typename... Args>
+ static inline auto initiate(Initiation&& initiation, RawCompletionToken&&, Args&&... args)
+ {
+ return async_initiate<Signatures...>(
+ std::forward<Initiation>(initiation),
+ Trinity::Asio::AsExpected<
+ default_completion_token_t<associated_executor_t<Initiation>>>{},
+ std::forward<Args>(args)...);
+ }
+};
+#else
+template <typename CompletionToken, typename Signature>
+class async_result<Trinity::Asio::AsExpected<CompletionToken>, Signature> : async_result<CompletionToken, typename Trinity::Asio::Impl::AsExpectedSignature<Signature>::type>
+{
+ template <typename Initiation>
+ struct init_wrapper
+ {
+ explicit init_wrapper(Initiation const& initiation) : initiation_(initiation) { }
+ explicit init_wrapper(Initiation&& initiation) : initiation_(std::move(initiation)) { }
+
+ template <typename Handler, typename... Args>
+ inline void operator()(Handler&& handler, Args&&... args) &&
+ {
+ static_cast<Initiation&&>(initiation_)(Trinity::Asio::Impl::AsExpectedHandler<std::decay_t<Handler>>(std::forward<Handler>(handler)), std::forward<Args>(args)...);
+ }
+
+ template <typename Handler, typename... Args>
+ inline void operator()(Handler&& handler, Args&&... args) const &
+ {
+ static_cast<Initiation const&>(initiation_)(Trinity::Asio::Impl::AsExpectedHandler<std::decay_t<Handler>>(std::forward<Handler>(handler)), std::forward<Args>(args)...);
+ }
+
+ Initiation initiation_;
+ };
+
+public:
+ template <typename Initiation, typename RawCompletionToken, typename... Args>
+ static inline auto initiate(Initiation&& initiation, RawCompletionToken&& token, Args&&... args)
+ {
+ return async_initiate<
+ conditional_t<
+ is_const<remove_reference_t<RawCompletionToken>>::value,
+ CompletionToken const, CompletionToken>,
+ typename Trinity::Asio::Impl::AsExpectedSignature<Signature>::type>(
+ init_wrapper<std::decay_t<Initiation>>(
+ std::forward<Initiation>(initiation)),
+ token.token_, std::forward<Args>(args)...);
+ }
+};
+#endif
+} // namespace boost::asio
+
+#endif // TRINITYCORE_EXPECTED_COMPLETION_HANDLER_H