with_diagnostics.hpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. //
  2. // Copyright (c) 2019-2025 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MYSQL_IMPL_WITH_DIAGNOSTICS_HPP
  8. #define BOOST_MYSQL_IMPL_WITH_DIAGNOSTICS_HPP
  9. #pragma once
  10. #include <boost/mysql/diagnostics.hpp>
  11. #include <boost/mysql/error_code.hpp>
  12. #include <boost/mysql/error_with_diagnostics.hpp>
  13. #include <boost/mysql/with_diagnostics.hpp>
  14. #include <boost/mysql/detail/intermediate_handler.hpp>
  15. #include <boost/asio/associated_allocator.hpp>
  16. #include <boost/asio/async_result.hpp>
  17. #include <boost/mp11/algorithm.hpp>
  18. #include <boost/mp11/list.hpp>
  19. #include <cstddef>
  20. #include <exception>
  21. #include <memory>
  22. #include <tuple>
  23. #include <type_traits>
  24. namespace boost {
  25. namespace mysql {
  26. namespace detail {
  27. struct with_diag_handler_fn
  28. {
  29. template <class Handler, class... Args>
  30. void operator()(Handler&& handler, error_code ec, Args&&... args)
  31. {
  32. std::exception_ptr exc = ec ? std::make_exception_ptr(error_with_diagnostics(ec, diag))
  33. : std::exception_ptr();
  34. owning_diag.reset();
  35. std::move(handler)(std::move(exc), std::forward<Args>(args)...);
  36. }
  37. // The diagnostics to use, taken from initiation
  38. const diagnostics& diag;
  39. // Keep alive any allocated diagnostics
  40. std::shared_ptr<diagnostics> owning_diag;
  41. };
  42. // By default, don't modify the signature.
  43. // This makes asio::as_tuple(with_diagnostics(X)) equivalent
  44. // to asio::as_tuple(X).
  45. template <typename Signature>
  46. struct with_diag_signature
  47. {
  48. using type = Signature;
  49. };
  50. template <typename R, typename... Args>
  51. struct with_diag_signature<R(error_code, Args...)>
  52. {
  53. using type = R(std::exception_ptr, Args...);
  54. };
  55. template <typename R, typename... Args>
  56. struct with_diag_signature<R(error_code, Args...)&>
  57. {
  58. using type = R(std::exception_ptr, Args...) &;
  59. };
  60. template <typename R, typename... Args>
  61. struct with_diag_signature<R(error_code, Args...) &&>
  62. {
  63. using type = R(std::exception_ptr, Args...) &&;
  64. };
  65. #if defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
  66. template <typename R, typename... Args>
  67. struct with_diag_signature<R(error_code, Args...) noexcept>
  68. {
  69. using type = R(std::exception_ptr, Args...) noexcept;
  70. };
  71. template <typename R, typename... Args>
  72. struct with_diag_signature<R(error_code, Args...) & noexcept>
  73. {
  74. using type = R(std::exception_ptr, Args...) & noexcept;
  75. };
  76. template <typename R, typename... Args>
  77. struct with_diag_signature<R(error_code, Args...) && noexcept>
  78. {
  79. using type = R(std::exception_ptr, Args...) && noexcept;
  80. };
  81. #endif // defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
  82. // Inheriting from Initiation propagates its executor type,
  83. // if any. Required by tokens like asio::cancel_after
  84. template <class Initiation>
  85. struct with_diag_init : public Initiation
  86. {
  87. template <class I>
  88. with_diag_init(I&& i) : Initiation(std::forward<I>(i))
  89. {
  90. }
  91. // We pass the inner token's initiation as 1st arg
  92. template <class Handler, class... Args>
  93. void operator()(Handler&& handler, Args&&... args) &&
  94. {
  95. // Find the diagnostics object in the list of arguments
  96. using types = mp11::mp_list<typename std::decay<Args>::type...>;
  97. constexpr std::size_t pos = mp11::mp_find<types, diagnostics*>::value;
  98. // If you're getting an error here, it's because you're trying to use
  99. // with_diagnostics with an async function unrelated to Boost.MySQL.
  100. static_assert(
  101. pos < mp11::mp_size<types>::value,
  102. "with_diagnostics only works with Boost.MySQL async functions"
  103. );
  104. // Actually get the object
  105. diagnostics*& diag = std::get<pos>(std::tuple<Args&...>{args...});
  106. // Some functions (e.g. connection_pool) may pass nullptr as diag.
  107. // When using this token, allocate a diagnostics instance and overwrite the passed value
  108. std::shared_ptr<diagnostics> owning_diag;
  109. if (!diag)
  110. {
  111. // The allocator to use
  112. auto base_alloc = asio::get_associated_allocator(handler);
  113. using alloc_type = typename std::allocator_traits<decltype(base_alloc
  114. )>::template rebind_alloc<diagnostics>;
  115. owning_diag = std::allocate_shared<diagnostics>(alloc_type{std::move(base_alloc)});
  116. diag = owning_diag.get();
  117. }
  118. // Actually initiate
  119. static_cast<Initiation&&>(*this)(
  120. make_intermediate_handler(
  121. with_diag_handler_fn{*diag, std::move(owning_diag)},
  122. std::forward<Handler>(handler)
  123. ),
  124. std::forward<Args>(args)...
  125. );
  126. }
  127. };
  128. // Did with_diagnostics modify any of the signatures?
  129. // We really support only modifying all or none, and that's enough.
  130. template <class Signature>
  131. using with_diag_has_original_signature = std::
  132. is_same<Signature, typename with_diag_signature<Signature>::type>;
  133. template <class... Signatures>
  134. using with_diag_has_original_signatures = mp11::
  135. mp_all_of<mp11::mp_list<Signatures...>, with_diag_has_original_signature>;
  136. template <typename CompletionToken, bool has_original_signatures, typename... Signatures>
  137. struct with_diagnostics_async_result;
  138. // async_result when the signature was modified
  139. template <typename CompletionToken, typename... Signatures>
  140. struct with_diagnostics_async_result<CompletionToken, false, Signatures...>
  141. : asio::async_result<CompletionToken, typename with_diag_signature<Signatures>::type...>
  142. {
  143. template <class RawCompletionToken>
  144. using maybe_const_token_t = typename std::conditional<
  145. std::is_const<typename std::remove_reference<RawCompletionToken>::type>::value,
  146. const CompletionToken,
  147. CompletionToken>::type;
  148. template <typename Initiation, typename RawCompletionToken, typename... Args>
  149. static auto initiate(Initiation&& initiation, RawCompletionToken&& token, Args&&... args)
  150. -> decltype(asio::async_initiate<
  151. maybe_const_token_t<RawCompletionToken>,
  152. typename with_diag_signature<Signatures>::type...>(
  153. with_diag_init<typename std::decay<Initiation>::type>{std::forward<Initiation>(initiation)},
  154. access::get_impl(token),
  155. std::forward<Args>(args)...
  156. ))
  157. {
  158. return asio::async_initiate<
  159. maybe_const_token_t<RawCompletionToken>,
  160. typename with_diag_signature<Signatures>::type...>(
  161. with_diag_init<typename std::decay<Initiation>::type>{std::forward<Initiation>(initiation)},
  162. access::get_impl(token),
  163. std::forward<Args>(args)...
  164. );
  165. }
  166. };
  167. // async_result when the signature wasn't modified (pass-through)
  168. template <typename CompletionToken, typename... Signatures>
  169. struct with_diagnostics_async_result<CompletionToken, true, Signatures...>
  170. : asio::async_result<CompletionToken, Signatures...>
  171. {
  172. template <class RawCompletionToken>
  173. using maybe_const_token_t = typename std::conditional<
  174. std::is_const<typename std::remove_reference<RawCompletionToken>::type>::value,
  175. const CompletionToken,
  176. CompletionToken>::type;
  177. template <typename Initiation, typename RawCompletionToken, typename... Args>
  178. static auto initiate(Initiation&& initiation, RawCompletionToken&& token, Args&&... args)
  179. -> decltype(asio::async_initiate<maybe_const_token_t<RawCompletionToken>, Signatures...>(
  180. std::forward<Initiation>(initiation),
  181. access::get_impl(token),
  182. std::forward<Args>(args)...
  183. ))
  184. {
  185. return asio::async_initiate<maybe_const_token_t<RawCompletionToken>, Signatures...>(
  186. std::forward<Initiation>(initiation),
  187. access::get_impl(token),
  188. std::forward<Args>(args)...
  189. );
  190. }
  191. };
  192. } // namespace detail
  193. } // namespace mysql
  194. namespace asio {
  195. template <typename CompletionToken, typename... Signatures>
  196. struct async_result<mysql::with_diagnostics_t<CompletionToken>, Signatures...>
  197. : mysql::detail::with_diagnostics_async_result<
  198. CompletionToken,
  199. mysql::detail::with_diag_has_original_signatures<Signatures...>::value,
  200. Signatures...>
  201. {
  202. };
  203. } // namespace asio
  204. } // namespace boost
  205. #endif