使用结果启动异步操作
Initiate an async operation with a result
我目前正在从事一个使用 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
但我想不出在 return 值不为空时将其放在哪里!
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??
我只是很困惑如何发送可等待对象的 return 以及如何启动带有结果的异步操作。
事实证明,异步操作可以采用许多固定签名。
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{});
}
的文档中有更多相关信息
我目前正在从事一个使用 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
但我想不出在 return 值不为空时将其放在哪里!
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??
我只是很困惑如何发送可等待对象的 return 以及如何启动带有结果的异步操作。
事实证明,异步操作可以采用许多固定签名。
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{});
}
的文档中有更多相关信息