【问题标题】:Initiate an async operation with a result使用结果启动异步操作
【发布时间】:2021-03-11 14:37:37
【问题描述】:

我目前正在开发一个使用 boost asio 进行网络的项目。我想实现一个如下所示的函数:

template<command T, response R = typename T::response_type>
auto send_command(T c) -> boost::asio::awaitable<R> {
    // ...
}

在我的代码中,当我将命令写入套接字时,我最终会收到带有结果的响应。在收到对特定命令的响应之前,我可能会在套接字中收到任意数量的其他响应。

我想我可以使用async_initiate 使发送命令成为由写入和读取组成的异步操作。

我已经使用async_initiate 启动异步操作,但它是boost::asio::awaitable&lt;void&gt; 类型。代码如下所示:

// returns the awaitable for the coroutine to suspend
return asio::async_initiate<CompletionToken, void(void)>(
    [self = shared_from_this()](auto&& handler) {
        self->callback_queue.push(std::forward<decltype(handler)>(handler));
    }
);

// later

auto& resume = callback_queue.front();
resume(); // resume the suspended coroutine

但是当它不是 void 时,我不知道将返回值放在哪里!

auto const command_id = std::int32_t{...};

auto& resume_command = commands_continuations[command_id];
resume_command(response_result); // Send the `co_await` result in parameters??

我只是对如何发送等待的返回以及如何使用结果启动异步操作感到困惑。

【问题讨论】:

  • 我知道传递给async_result 的签名决定了你的令牌类型的特化最终推导出的返回类型。我与async_initiate 并不亲密——我发现那部分的文档非常脆弱(基本上不存在)。所以,我不能为 use_awaitable 显示它,但也许它有助于显示其他类型的完成标记的类似物:wandbox.org/permlink/ApEp5L3nu1F1eFXZ

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


【解决方案1】:

事实证明,异步操作可以采用许多固定签名。

void(void) 是一个,但要得到结果,我们可以简单地使用void(T):

return asio::async_initiate<CompletionToken, void(R)>(
    [self = shared_from_this()](auto&& handler) {
        self->callback_queue.push(std::forward<decltype(handler)>(handler));
    }
);

handler 将是一个以R 作为参数的可调用对象,并将成为co_await 操作的结果。

为了进行异常处理和取消,我尝试了两种不同的方式来传播异常。一种是发送std::exception_ptr,另一种是使用boost::system::error_code。就我而言,我选择错误代码是因为我更喜欢使用它们。

return asio::async_initiate<CompletionToken, void(boost::system::error_code, R)>(
    [self = shared_from_this()](auto&& handler) {
        self->callback_queue.push(std::forward<decltype(handler)>(handler));
    }
);

回调采用与签名void(boost::system::error_code, R) 匹配的仅移动函数的形式。我这样称呼它:

auto callback = std::exchange(callback_queue[command_id], {});
if (result) {
    // resumes the coroutine normally with the result
    callback(boost::system::error_code{}, *result);
} else {
    // Throws exception in the coroutine
    callback(make_error_code(boost::system::errc::operation_canceled), R{});
}

关于ASIO/Networking TS的文档中有更多关于它的信息

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-21
    • 1970-01-01
    • 2016-05-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多