[英]Wrapping boost async function into coroutine
嘿,我正在尝试包装第三方库提供的 class 以使用 Boost 协程。 该库也使用 Boost,但出于异步操作的目的,使用了完成处理程序。 下面是一个可以尝试的简化示例。 我想我已经接近了,但由于某种原因,从 async_connect 返回的 awaitable 是 void 类型,而我想 boost::error_code 被返回。 我错过了什么?
#include <boost/asio.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <iostream>
using AsyncHandler = std::function<void(boost::system::error_code)>;
struct LibraryClient
{
LibraryClient(boost::asio::io_context& ioc)
: socket{ioc}
{}
boost::asio::ip::tcp::socket socket;
void async_connect(AsyncHandler handler = {})
{
boost::system::error_code ec;
boost::asio::ip::address ip_address = boost::asio::ip::address::from_string("127.0.0.1", ec);
boost::asio::ip::tcp::endpoint ep(ip_address, 9999);
socket.async_connect(ep, std::move(handler));
}
};
template<class CompletitionToken = boost::asio::use_awaitable_t<>>
auto do_async_connect(LibraryClient& client, CompletitionToken&& token = {})
{
auto initiate = [&client]<class H>(H&& self) mutable
{
client.async_connect([self = std::make_shared<H>(std::forward<H>(self))](auto&& r)
{
(*self)(r);
});
};
return boost::asio::async_initiate<
CompletitionToken, boost::system::error_code(boost::system::error_code)
>(initiate, token);
}
struct LibraryClientWrapper
{
LibraryClient client;
boost::asio::awaitable<boost::system::error_code> async_connect()
{
//auto ec = co_await do_something_with_client();
const auto ec = co_await do_async_connect(client);
}
};
int main()
{
auto ioc = boost::asio::io_context{};
auto client = LibraryClientWrapper{LibraryClient{ioc}};
ioc.run();
}
编辑似乎我发现了一些东西。 我稍微修改了代码,删除了本示例不需要的所有代码
#include <boost/asio.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <iostream>
#include <cassert>
template<class CompletitionToken>
auto do_async_connect(LibraryClient& client, CompletitionToken&& token)
{
auto initiate = [](auto&& handler) {
handler(nullptr, 90);
};
return boost::asio::async_initiate<CompletitionToken, void(Eptr, int)>(
initiate, std::forward<CompletitionToken>(token)
);
}
struct LibraryClientWrapper
{
LibraryClient client;
boost::asio::awaitable<void> async_connect()
{
const auto ec = co_await do_async_connect(client, boost::asio::use_awaitable);
assert(ec == 90);
}
};
void rethrow_exception(std::exception_ptr eptr)
{
if (eptr)
{
std::rethrow_exception(eptr);
}
}
int main()
{
auto ioc = boost::asio::io_context{};
auto client = LibraryClientWrapper{LibraryClient{ioc}};
boost::asio::co_spawn(ioc, client.async_connect(), rethrow_exception);
ioc.run();
}
如您所见,我将 signatute 更改为同时采用 std::exception_ptr 和 int,这导致 int 从协程中正确返回。 但我不明白为什么需要这个签名,特别是 std::exception_ptr 作为第一个参数。
好吧,我设法让我们说解决它。 但我不明白为什么处理程序的签名必须采用 void(std:::exception_ptr, error_code) 的形式才能返回 error_code。 我也未能在 boost 文档中找到它。 如果有人能提供某种解释,我将不胜感激。
#include <boost/asio.hpp>
#include <iostream>
using AsyncHandler = std::function<void(boost::system::error_code)>;
void rethrow_exception(std::exception_ptr eptr)
{
if (eptr)
{
std::rethrow_exception(eptr);
}
}
using Eptr = std::exception_ptr;
struct LibraryClient
{
LibraryClient(LibraryClient&&) = default;
LibraryClient(boost::asio::io_context& ioc)
: socket{ioc}
{}
boost::asio::ip::tcp::socket socket;
void async_connect(AsyncHandler handler = {})
{
boost::system::error_code ec;
boost::asio::ip::address ip_address = boost::asio::ip::address::from_string("35.156.20.134", ec);
boost::asio::ip::tcp::endpoint ep(ip_address, 1883);
socket.async_connect(ep, std::move(handler));
}
};
template<typename CompletitionHandler = boost::asio::use_awaitable_t<>>
auto do_async_connect(LibraryClient& client, CompletitionHandler&& handler = {})
{
auto initiate = [&client]<class Handler>(Handler&& self) mutable
{
client.async_connect([self = std::make_shared<Handler>(std::forward<Handler>(self))](auto error_code)
{
(*self)(std::current_exception(), error_code);
});
};
return boost::asio::async_initiate<CompletitionHandler, void(Eptr, boost::system::error_code)>(initiate, handler);
}
struct LibraryClientWrapper
{
LibraryClient client;
boost::asio::awaitable<void> async_connect()
{
const auto ec = co_await do_async_connect(client, boost::asio::use_awaitable);
std::cout << ec.message() << std::endl;
}
};
int main()
{
auto ioc = boost::asio::io_context{};
auto client = LibraryClientWrapper{LibraryClient{ioc}};
boost::asio::co_spawn(ioc, client.async_connect(), rethrow_exception);
ioc.run();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.