// // Copyright (c) 2019-2025 Ruben Perez Hidalgo (rubenperez038 at gmail 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 BOOST_MYSQL_IMPL_WITH_DIAGNOSTICS_HPP #define BOOST_MYSQL_IMPL_WITH_DIAGNOSTICS_HPP #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace mysql { namespace detail { struct with_diag_handler_fn { template void operator()(Handler&& handler, error_code ec, Args&&... args) { std::exception_ptr exc = ec ? std::make_exception_ptr(error_with_diagnostics(ec, diag)) : std::exception_ptr(); owning_diag.reset(); std::move(handler)(std::move(exc), std::forward(args)...); } // The diagnostics to use, taken from initiation const diagnostics& diag; // Keep alive any allocated diagnostics std::shared_ptr owning_diag; }; // By default, don't modify the signature. // This makes asio::as_tuple(with_diagnostics(X)) equivalent // to asio::as_tuple(X). template struct with_diag_signature { using type = Signature; }; template struct with_diag_signature { using type = R(std::exception_ptr, Args...); }; template struct with_diag_signature { using type = R(std::exception_ptr, Args...) &; }; template struct with_diag_signature { using type = R(std::exception_ptr, Args...) &&; }; #if defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE) template struct with_diag_signature { using type = R(std::exception_ptr, Args...) noexcept; }; template struct with_diag_signature { using type = R(std::exception_ptr, Args...) & noexcept; }; template struct with_diag_signature { using type = R(std::exception_ptr, Args...) && noexcept; }; #endif // defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE) // Inheriting from Initiation propagates its executor type, // if any. Required by tokens like asio::cancel_after template struct with_diag_init : public Initiation { template with_diag_init(I&& i) : Initiation(std::forward(i)) { } // We pass the inner token's initiation as 1st arg template void operator()(Handler&& handler, Args&&... args) && { // Find the diagnostics object in the list of arguments using types = mp11::mp_list::type...>; constexpr std::size_t pos = mp11::mp_find::value; // If you're getting an error here, it's because you're trying to use // with_diagnostics with an async function unrelated to Boost.MySQL. static_assert( pos < mp11::mp_size::value, "with_diagnostics only works with Boost.MySQL async functions" ); // Actually get the object diagnostics*& diag = std::get(std::tuple{args...}); // Some functions (e.g. connection_pool) may pass nullptr as diag. // When using this token, allocate a diagnostics instance and overwrite the passed value std::shared_ptr owning_diag; if (!diag) { // The allocator to use auto base_alloc = asio::get_associated_allocator(handler); using alloc_type = typename std::allocator_traits::template rebind_alloc; owning_diag = std::allocate_shared(alloc_type{std::move(base_alloc)}); diag = owning_diag.get(); } // Actually initiate static_cast(*this)( make_intermediate_handler( with_diag_handler_fn{*diag, std::move(owning_diag)}, std::forward(handler) ), std::forward(args)... ); } }; // Did with_diagnostics modify any of the signatures? // We really support only modifying all or none, and that's enough. template using with_diag_has_original_signature = std:: is_same::type>; template using with_diag_has_original_signatures = mp11:: mp_all_of, with_diag_has_original_signature>; template struct with_diagnostics_async_result; // async_result when the signature was modified template struct with_diagnostics_async_result : asio::async_result::type...> { template using maybe_const_token_t = typename std::conditional< std::is_const::type>::value, const CompletionToken, CompletionToken>::type; template static auto initiate(Initiation&& initiation, RawCompletionToken&& token, Args&&... args) -> decltype(asio::async_initiate< maybe_const_token_t, typename with_diag_signature::type...>( with_diag_init::type>{std::forward(initiation)}, access::get_impl(token), std::forward(args)... )) { return asio::async_initiate< maybe_const_token_t, typename with_diag_signature::type...>( with_diag_init::type>{std::forward(initiation)}, access::get_impl(token), std::forward(args)... ); } }; // async_result when the signature wasn't modified (pass-through) template struct with_diagnostics_async_result : asio::async_result { template using maybe_const_token_t = typename std::conditional< std::is_const::type>::value, const CompletionToken, CompletionToken>::type; template static auto initiate(Initiation&& initiation, RawCompletionToken&& token, Args&&... args) -> decltype(asio::async_initiate, Signatures...>( std::forward(initiation), access::get_impl(token), std::forward(args)... )) { return asio::async_initiate, Signatures...>( std::forward(initiation), access::get_impl(token), std::forward(args)... ); } }; } // namespace detail } // namespace mysql namespace asio { template struct async_result, Signatures...> : mysql::detail::with_diagnostics_async_result< CompletionToken, mysql::detail::with_diag_has_original_signatures::value, Signatures...> { }; } // namespace asio } // namespace boost #endif