Я пытаюсь переписать проект с помощью boost::asio::spawn
сопрограммы. Некоторые части проекта не могут быть изменены. Например, библиотека протокола хранения также написана с boost::asio
, но без сопрограмм.
Проблема в том, как конвертировать yield_context
в нормальный обратный вызов ( boost::function
объект или классический функтор).
Вот что мы имеем в API библиотеки хранилищ:
void async_request_data(uint64_t item_id, boost::function< void(Request_result *) > callback);
Как мы знаем из примеров, контекст asio yield можно использовать так:
my_socket.async_read_some(boost::asio::buffer(data), yield);
В этом случае boost::asio::yield_context
Объект служит обратным вызовом для async_read_some. Я хотел бы передать Уступать объект в качестве второго аргумента async_request_data
так что я могу использовать его синхронно.
Как это может быть сделано? Я думаю, что это может быть возможно через некоторый прокси-объект, возможно, с использованием подхода, основанного на asio_handler_invoke. Но я не могу понять, как это сделать.
Похоже, лучшую документацию по этой функции можно найти в стандартном предложении C ++, написанном автором boost asio:
N4045 — Основы библиотеки для асинхронных операций, редакция 2
Смотрите раздел 9.1, в котором говорится:
handler_type_t<CompletionToken, void(error_code, size_t)> #3
handler(std::forward<CompletionToken>(token));
3: токен завершения преобразуется в обработчик, то есть вызываемый объект функции
когда асинхронная операция завершается. Подпись указывает аргументы
это будет передано обработчику.
Я думаю, в вашем случае CompletionToken
Шаблонный аргумент на самом деле будет boost::asio::yield_context
а также handler_type
преобразует его в объект обратного вызова.
Вот код из раздела 9.1 обновлен, чтобы позвонить вашему async_request_data
функция:
template <class CompletionToken>
auto async_foo(uint64_t item_id, CompletionToken&& token)
{
handler_type_t<CompletionToken, void(Request_result *)>
handler(std::forward<CompletionToken>(token));
async_result<decltype(handler)> result(handler);
async_request_data(item_id, handler);
return result.get();
}
Благодаря @PSIAlt и @free_coffee я знаю, как использовать функции обратного вызова в стековой сопрограмме.
Вот простой пример только для новичков Asio (как я: D)
https://gist.github.com/chenfengyuan/4d764b0bca82a42c05a9
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio/spawn.hpp>
#include <memory>
void bar(boost::asio::io_service &io, std::function<void()> cb){
auto ptr = std::make_shared<boost::asio::deadline_timer>(io, boost::posix_time::seconds(1));
ptr->async_wait([ptr, cb](const boost::system::error_code&){cb();});
}
template<typename Handler>
void foo(boost::asio::io_service &io, Handler && handler){
typename boost::asio::handler_type<Handler, void()>::type handler_(std::forward<Handler>(handler));
boost::asio::async_result<decltype(handler_)> result(handler_);
bar(io, handler_);
result.get();
return;
}
int main()
{
boost::asio::io_service io;
boost::asio::spawn(io, [&io](boost::asio::yield_context yield){
foo(io, yield);
std::cout << "hello, world!\n";
});
io.run();
return 0;
}
Большое спасибо free_coffe мне удалось это сработать. Выкладываю решение для моего случая, возможно кому-то это нужно.
template <class CompletionToken>
RequestResult async_foo(Packet &pkt, CompletionToken&& token) {
typename boost::asio::handler_type< CompletionToken, void(RequestResult) >::type handler( std::forward<CompletionToken>(token) );
boost::asio::async_result<decltype(handler)> result(handler);
storage_api->writePacket(pkt, handler);
return result.get();
}
Позже мы можем использовать этот прокси:
RequestResult res = async_foo(pkt, std::forward<boost::asio::yield_context>(yield) );