首页 > 解决方案 > asio 自定义异步函数和 c++20 协程

问题描述

我正在将我的 asio 应用程序从它的 stackful coroutines 迁移到 c++20 stackless coroutines。我有一个现有的类方法,如下所示:

int my_async_op(asio::yield_context yield) {
    using tResult = asio::async_result<asio::yield_context, void(boost::system::error_code, int)>;
    tResult::completion_handler_type handler(yield);
    tResult result(handler);
    ...
    boost::system::error_code ec;
    asio::some_async_op(yield[ec]);
    ...
    handler(boost::system::error_code(), 42);
    return result.get();
}

...并像这样调用:

boost::system::error_code ec;
x = my_async_op(yield[ec]);

当迁移到 C++20 无堆栈协同程序时,需要链接,我的函数的骨架现在看起来像这样:

asio::awaitable<int> my_async_op(...) {
    ...
    boost::system::error_code ec;
    co_await asio::some_async_op(net::redirect_error(net::use_awaitable, ec));
    ...
    co_return 42;
}

...但是这样称呼:

boost::system::error_code ec;
x = co_await my_async_op(net::redirect_error(net::use_awaitable, ec));

因此,骨架需要更新以获取完成令牌,与本机 asio 异步操作相同,但我可以找到一个参考示例来解决,我承认发现 asio 源代码难以解析。

任何指导或参考将不胜感激。

编辑:我想我已经接近 asio::async_initiate per http://open-std.org/JTC1/SC22/WG21/docs/papers/2019/p1943r0.html。我的函数现在看起来像这样:

template<typename T>
auto my_async_op<T&& token) {
    return asio::async_initiate<T, void(boost::system::error_code, int)>(
        [&](auto handler)->void {
            ...
            boost::system::error_code ec;
            co_await asio::some_async_op(asio::redirect_error(asio::use_awaitable, ec));
            ...
            handler(boost::system::error_code(), 42);
        },
        token
    );
}

唯一的问题是我在嵌套的 co_await 调用上遇到编译器错误:

XXX.h:97:12: error: unable to find the promise type for this coroutine
   97 | co_await asio::some_async_op(net::redirect_error(net::use_awaitable, ec));

会一直磨下去。

编辑:现在调查这个...... https://github.com/chriskohlhoff/asio/issues/795

标签: c++c++20coroutineasio

解决方案


如果您的要求是编写自己的 async_xyz 函数,则可以使用 async_initiate。您应该使用另一个线程来运行该功能。

我这样做是为了计算提升线程池上的哈希值。

例如:

template <boost::asio::completion_token_for<void (std::string)> CompletionToken>
auto
async_hash (boost::asio::thread_pool &pool, boost::asio::io_context &io_context, std::string const &password, CompletionToken &&token)
{
  return boost::asio::async_initiate<CompletionToken, void (std::string)> (
      [&] (auto completion_handler, std::string const &passwordToHash) {
        auto io_eq = boost::asio::prefer (io_context.get_executor (), boost::asio::execution::outstanding_work.tracked);
        boost::asio::post (pool, [&, io_eq = std::move (io_eq), completion_handler = std::move (completion_handler), passwordToHash] () mutable {
          auto hashedPw = pw_to_hash (passwordToHash);
          boost::asio::post (io_eq, [hashedPw = std::move (hashedPw), completion_handler = std::move (completion_handler)] () mutable { completion_handler (hashedPw); });
        });
      },
      token, password);
}

在调用端,您可以像协程中的其他 async_xyz 函数一样调用它:

auto hashedPw = co_await async_hash (pool, io_context, createAccountObject.password, boost::asio::use_awaitable);

推荐阅读