admin管理员组

文章数量:1200771

Clang tells me there is no viable conversion from the return value of boost::beast::http::async_write with completion token boost::asio::as_tuple(boost::asio::deferred) to awaitable<tuple<boost::system::error_code, unsigned long>>:

error: no viable conversion from returned value of type 'decltype(enable_if_t<enable_if_t<detail::are_completion_signatures<void (error_code, unsigned long)>::value, detail::async_result_has_initiate_memfn<as_tuple_t<deferred_t>, void (error_code, unsigned long)>>::value, async_result<decay_t<as_tuple_t<deferred_t>>, void (error_code, unsigned long)>>::initiate(static_cast<boost::beast::http::detail::run_write_msg_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp>> &&>(initiation), static_cast<boost::asio::as_tuple_t<boost::asio::deferred_t> &&>(token), static_cast<const boost::beast::http::message<false, boost::beast::http::basic_string_body<char>> *&&>(args), static_cast<std::integral_constant<bool, true> &&>(args)))' (aka 'deferred_async_operation<void (std::tuple<boost::system::error_code, unsigned long>), boost::asio::async_result<boost::asio::as_tuple_t<boost::asio::deferred_t>, void (boost::system::error_code, unsigned long)>::init_wrapper<boost::beast::http::detail::run_write_msg_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>>>, const boost::beast::http::message<false, boost::beast::http::basic_string_body<char, std::char_traits<char>, std::allocator<char>>, boost::beast::http::basic_fields<std::allocator<char>>> *, std::integral_constant<bool, true>>') to function return type 'boost::asio::awaitable<std::tuple<boost::system::error_code, std::size_t>>' (aka 'awaitable<tuple<boost::system::error_code, unsigned long>>')
  179 |return boost::beast::http::async_write(stream, typed_message, boost::asio::as_tuple(boost::asio::deferred));
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The function in question looks like this:

template<typename stream_t>
boost::asio::awaitable<void> response_worker(boost::asio::experimental::channel<void(boost::system::error_code, http_response_t)> &channel, stream_t &stream)
{
    while (true) {
        boost::system::error_code ec;
        std::variant<
            boost::beast::http::response<boost::beast::http::string_body>,
            boost::beast::http::response<boost::beast::http::empty_body>,
            boost::beast::http::response<boost::beast::http::dynamic_body>,
            boost::beast::http::response<boost::beast::http::file_body>,
            boost::beast::http::response<boost::beast::http::buffer_body>>
            message = co_await channel.async_receive(boost::asio::redirect_error(boost::asio::deferred, ec));
        if (ec == boost::asio::error::eof) {
            channel.close();
            co_return;
        }
        const auto [ec2, transferred] = co_await std::visit(
            [&stream](auto &typed_message) -> boost::asio::awaitable<std::tuple<boost::system::error_code, std::size_t>> {
                return boost::beast::http::async_write(stream, typed_message, boost::asio::as_tuple(boost::asio::deferred));
            },
            message);
        //error handling
    }
}

This used to compile and I'm incredibly confused by the error message, is there an error in my code or is my clang installation even more broken than I thought (libclang does not get the header search correct means I have to add -I/usr/lib/clang/19/include as a compiler flag if I understand it right)?

Clang tells me there is no viable conversion from the return value of boost::beast::http::async_write with completion token boost::asio::as_tuple(boost::asio::deferred) to awaitable<tuple<boost::system::error_code, unsigned long>>:

error: no viable conversion from returned value of type 'decltype(enable_if_t<enable_if_t<detail::are_completion_signatures<void (error_code, unsigned long)>::value, detail::async_result_has_initiate_memfn<as_tuple_t<deferred_t>, void (error_code, unsigned long)>>::value, async_result<decay_t<as_tuple_t<deferred_t>>, void (error_code, unsigned long)>>::initiate(static_cast<boost::beast::http::detail::run_write_msg_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp>> &&>(initiation), static_cast<boost::asio::as_tuple_t<boost::asio::deferred_t> &&>(token), static_cast<const boost::beast::http::message<false, boost::beast::http::basic_string_body<char>> *&&>(args), static_cast<std::integral_constant<bool, true> &&>(args)))' (aka 'deferred_async_operation<void (std::tuple<boost::system::error_code, unsigned long>), boost::asio::async_result<boost::asio::as_tuple_t<boost::asio::deferred_t>, void (boost::system::error_code, unsigned long)>::init_wrapper<boost::beast::http::detail::run_write_msg_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>>>, const boost::beast::http::message<false, boost::beast::http::basic_string_body<char, std::char_traits<char>, std::allocator<char>>, boost::beast::http::basic_fields<std::allocator<char>>> *, std::integral_constant<bool, true>>') to function return type 'boost::asio::awaitable<std::tuple<boost::system::error_code, std::size_t>>' (aka 'awaitable<tuple<boost::system::error_code, unsigned long>>')
  179 |return boost::beast::http::async_write(stream, typed_message, boost::asio::as_tuple(boost::asio::deferred));
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The function in question looks like this:

template<typename stream_t>
boost::asio::awaitable<void> response_worker(boost::asio::experimental::channel<void(boost::system::error_code, http_response_t)> &channel, stream_t &stream)
{
    while (true) {
        boost::system::error_code ec;
        std::variant<
            boost::beast::http::response<boost::beast::http::string_body>,
            boost::beast::http::response<boost::beast::http::empty_body>,
            boost::beast::http::response<boost::beast::http::dynamic_body>,
            boost::beast::http::response<boost::beast::http::file_body>,
            boost::beast::http::response<boost::beast::http::buffer_body>>
            message = co_await channel.async_receive(boost::asio::redirect_error(boost::asio::deferred, ec));
        if (ec == boost::asio::error::eof) {
            channel.close();
            co_return;
        }
        const auto [ec2, transferred] = co_await std::visit(
            [&stream](auto &typed_message) -> boost::asio::awaitable<std::tuple<boost::system::error_code, std::size_t>> {
                return boost::beast::http::async_write(stream, typed_message, boost::asio::as_tuple(boost::asio::deferred));
            },
            message);
        //error handling
    }
}

This used to compile and I'm incredibly confused by the error message, is there an error in my code or is my clang installation even more broken than I thought (libclang does not get the header search correct means I have to add -I/usr/lib/clang/19/include as a compiler flag if I understand it right)?

Share Improve this question asked Jan 21 at 21:26 climb4climb4 3141 silver badge8 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2

Reading the compiler message:

error: no viable conversion from returned value of type 'decltype(...)` (aka 
    deferred_async_operation<
        void(std::tuple<boost::system::error_code, unsigned long>),
        asio::async_result<asio::as_tuple_t<asio::deferred_t>,
                                  void(boost::system::error_code, unsigned long)>::
            init_wrapper<http::detail::run_write_msg_op<
                asio::basic_stream_socket<asio::ip::tcp, asio::any_io_executor>>>,
        http::message<
            false, http::basic_string_body<char, std::char_traits<char>, std::allocator<char>>,
            http::basic_fields<std::allocator<char>>> const*,
        std::integral_constant<bool, true>>'
          )
              to function return type
      'asio::awaitable<std::tuple<boost::system::error_code, std::size_t>>'
      return http::async_write(stream, typed_message,
                                             asio::as_tuple(asio::deferred));

It looks like you may have changed from asio::use_awaitable (which does return an awaitable promise type), to asio::deferred (which results in a deferred async operation).

This is good, because

  1. deferred became the default completion token
  2. deferred incurs less overhead when called ("transformed") from inside an Asio coroutine frame (awaitable promise type)

This means you will have to await the deferred operation to get a compatible return type. Note also that recent Asio versions have included partial applications of the token adaptors so you can spell it like so:

Live On Coliru

while (true) {
    if (auto [ec, message] = co_await channel.async_receive(asio::as_tuple); ec == asio::error::eof) {
        co_return channel.close();
    } else if (auto [ec, transferred] = co_await std::visit(
                   [&](auto& typed_message) -> asio::awaitable<std::tuple<error_code, std::size_t>> {
                       co_return co_await async_write(stream, typed_message, asio::as_tuple);
                   },
                   message);
               ec.failed()) //
    {
        // error handling
    } else {
        // success handling
    }
}

HOLD ON

However, please note that you don't need to be so ... clumsy about variant response objects. You can type-erase the response using a BuffersGenerator. The implementation that supports all message<> instances is called http::message_generator. E.g.:

#include <boost/asio.hpp>
#include <boost/asio/experimental/channel.hpp>
#include <boost/beast.hpp>
namespace asio = boost::asio;

namespace http        = boost::beast::http;
using http_response_t = std::optional<http::message_generator>;
using channel_t       = asio::experimental::channel<void(boost::system::error_code, http_response_t)>;

template <typename stream_t> asio::awaitable<void> response_worker(channel_t& channel, stream_t& stream) {
    using boost::beast::error_code;

    while (true) {
        if (auto [ec, response] = co_await channel.async_receive(asio::as_tuple); ec == asio::error::eof) {
            co_return channel.close();
        } else if (response.has_value()) {
            if (auto [ec, transferred] =
                    co_await boost::beast::async_write(stream, std::move(*response), asio::as_tuple);
                ec.failed()) //
            {
                // error handling
            } else {
                // success handling
            }
        }
    }
}

int main() {
    asio::io_context      io_context;
    asio::ip::tcp::socket socket(io_context); // TODO connect
    channel_t             channel(io_context);

    co_spawn(io_context, response_worker(channel, socket), asio::detached);

    io_context.run();
}

本文标签: