启动异步操作并返回结果



我目前正在进行一个使用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<void>。代码看起来是这样的:

// 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

但当它不是空的时候,我不知道该把返回值放在哪里!

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??

我只是困惑于如何发送awaitable的返回以及如何启动异步操作并得到结果。

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

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 的文档中有更多信息

最新更新