【问题标题】:co_await custom awaiter in boost asio coroutineboost asio 协程中的 co_await 自定义等待器
【发布时间】:2021-11-15 17:04:05
【问题描述】:

我目前正在尝试将新的 C++20 协程与 boost::asio 一起使用。但是,我正在努力找出如何实现自定义等待函数(例如 boost::asio::read_async)。我要解决的问题如下:

我有一个连接对象,我可以在其中发出多个请求并为响应注册回调。不能保证响应按照请求的顺序到达。我尝试用自定义的可等待对象包装回调,但是我无法在协程中 co_await 这个,因为我的可等待类型在 boost::asio::awaitable 中没有 await_transform。

我试图将回调包装成可等待的代码改编自这里: https://books.google.de/books?id=tJIREAAAQBAJ&pg=PA457

auto async_request(const request& r)
{
    struct awaitable
    {
        client* cli;
        request req;
        response resp{};

        bool await_ready() { return false; }
        void await_suspend(std::coroutine_handle<> h)
        {
            cli->send(req, [this, h](const response& r)
            {
                resp = r;
                h.resume();
            });
        }
        auto await_resume()
        {
            return resp;
        }
    };
    return awaitable{this, r};
}

我尝试像这样调用 boost 协程:

boost::asio::awaitable<void> network::sts::client::connect()
{
    //...
    auto res = co_await async_request(make_sts_connect());
    //...
}

给我以下错误:

error C2664: 'boost::asio::detail::awaitable_frame_base<Executor>::await_transform::result boost::asio::detail::awaitable_frame_base<Executor>::await_transform(boost::asio::this_coro::executor_t) noexcept': cannot convert argument 1 from 'network::sts::client::async_request::awaitable' to 'boost::asio::this_coro::executor_t'

有没有办法实现这个功能?

【问题讨论】:

    标签: c++ boost c++20 asio c++-coroutine


    【解决方案1】:

    我实际上设法找到了解决方案。做到这一点的方法是使用boost::asio::async_initiate 构造一个延续处理程序,并将处理程序默认为boost::use_awaitable。作为一个额外的好处,通过简单地使用处理程序的模板参数将其与其他异步函数匹配是微不足道的。

    template<typename ResponseHandler = boost::asio::use_awaitable_t<>>
    auto post(const request& req, ResponseHandler&& handler = {})
    {
        auto initiate = [this]<typename Handler>(Handler&& self, request req) mutable
        {
            send(req, [self = std::make_shared<Handler>(std::forward<Handler>(self))](const response& r)
            {
                (*self)(r);
            });
        };
        return boost::asio::async_initiate<
            ResponseHandler, void(const response&)>(
                initiate, handler, req
            );
    }
    

    这里唯一的问题是 std::function 显然没有移动构造,所以我不得不将处理程序包装在 std::shared_ptr 中。

    【讨论】:

    • 您能否详细说明一下这究竟是如何解决您的问题的,以及各个部分是如何组合在一起的?非常感谢!
    • 当然。基本上你可以做co_await post(req); 或使用任何其他“asio”方式,比如auto fut = post(req, boost::asio::use_future)async_initiate 将设置所有 asio 特定的东西,然后使用您的参数和结果处理程序调用 initiate 函数。 send 函数是一个简单的基于回调的异步函数,它会在结果可用时立即调用结果处理程序,然后 boost 会处理协程内容。
    猜你喜欢
    • 2021-04-23
    • 2021-06-28
    • 1970-01-01
    • 2020-05-27
    • 2014-02-08
    • 1970-01-01
    • 1970-01-01
    • 2021-04-16
    • 2011-10-10
    相关资源
    最近更新 更多