with_diagnostics.hpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  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_WITH_DIAGNOSTICS_HPP
  8. #define BOOST_MYSQL_WITH_DIAGNOSTICS_HPP
  9. #include <boost/mysql/detail/access.hpp>
  10. #include <type_traits>
  11. #include <utility>
  12. namespace boost {
  13. namespace mysql {
  14. /**
  15. * \brief A completion token adapter used to include server diagnostics in exceptions.
  16. * \details
  17. * When passed to an async initiating function, transforms its handler signature from `void(error_code, T...)`
  18. * to `void(std::exception_ptr, T...)`.
  19. * Uses knowledge of Boost.MySQL internals to grab any \ref diagnostics
  20. * that the operation may produce to create a `std::exception_ptr` pointing to an \ref error_with_diagnostics
  21. * object. On success, the generated `std::exception_ptr` will be `nullptr`.
  22. *
  23. * Using `with_diagnostics` to wrap tokens that throw exceptions (like `deferred` + `co_await` or
  24. * `yield_context`) enhances the thrown exceptions with diagnostics information, matching the ones thrown by
  25. * sync functions. If you don't use this token, Asio will use `system_error` exceptions, containing less info.
  26. *
  27. * This token can only be used with operations involving Boost.MySQL, as it relies on its internals.
  28. *
  29. * Like `asio::as_tuple`, this class wraps another completion token. For instance,
  30. * `with_diagnostics(asio::deferred)` will generate a deferred operation with an adapted
  31. * signature, which will throw `error_with_diagnostics` when `co_await`'ed.
  32. *
  33. * If this token is applied to a function with a handler signature that
  34. * does not match `void(error_code, T...)`, the token acts as a pass-through:
  35. * it does not modify the signature, and calls the underlying token's initiation directly.
  36. * This has the following implications:
  37. *
  38. * - `asio::as_tuple(with_diagnostics(X))` is equivalent to `asio::as_tuple(X)`.
  39. * - `asio::redirect_error(with_diagnostics(X))` is equivalent to `asio::redirect_error(X)`.
  40. * - Tokens like `asio::as_tuple` and `asio::redirect_error` can be used as partial tokens
  41. * when `with_diagnostics` is the default completion token, as is the case for \ref any_connection.
  42. */
  43. template <class CompletionToken>
  44. class with_diagnostics_t
  45. {
  46. CompletionToken impl_;
  47. #ifndef BOOST_MYSQL_DOXYGEN
  48. friend struct detail::access;
  49. #endif
  50. public:
  51. /**
  52. * \brief Default constructor.
  53. * \details
  54. * Only valid if `CompletionToken` is default-constructible.
  55. *
  56. * \par Exception safety
  57. * Strong guarantee. Any exceptions thrown by default-constructing
  58. * `CompletionToken` are propagated.
  59. */
  60. constexpr with_diagnostics_t() : impl_{} {}
  61. /**
  62. * \brief Constructor.
  63. * \details
  64. * The passed `token` should be convertible to `CompletionToken`.
  65. *
  66. * \par Exception safety
  67. * Strong guarantee. Any exceptions thrown by constructing `CompletionToken` are propagated.
  68. */
  69. template <class T>
  70. constexpr explicit with_diagnostics_t(T&& token) : impl_(std::forward<T>(token))
  71. {
  72. }
  73. };
  74. /**
  75. * \brief Creates a \ref with_diagnostics_t from a completion token.
  76. * \details
  77. * The passed token is decay-copied into the \ref with_diagnostics_t object.
  78. *
  79. * \par Exception safety
  80. * Strong guarantee. Any exceptions thrown by constructing `CompletionToken` are propagated.
  81. */
  82. template <class CompletionToken>
  83. constexpr with_diagnostics_t<typename std::decay<CompletionToken>::type> with_diagnostics(
  84. CompletionToken&& token
  85. )
  86. {
  87. return with_diagnostics_t<typename std::decay<CompletionToken>::type>(std::forward<CompletionToken>(token)
  88. );
  89. }
  90. } // namespace mysql
  91. } // namespace boost
  92. #include <boost/mysql/impl/with_diagnostics.hpp>
  93. #endif