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)?
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
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:
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
}
}
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();
}