Подтвердить что ты не робот

Boost:: asio:: spawn yield в качестве обратного вызова

Я пытаюсь переписать проект, используя 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 можно использовать следующим образом:

    my_socket.async_read_some(boost::asio::buffer(data), yield);

В этом случае объект boost::asio::yield_context служит для обратного вызова для async_read_some. Я хотел бы передать объект yield в качестве второго аргумента async_request_data, поэтому я могу использовать его синхронно.

Как это можно сделать? Я думаю, что это возможно с помощью некоторого прокси-объекта, возможно, используя подход, основанный на asio_handler_invoke. Но я не могу понять, как это сделать.

4b9b3361

Ответ 1

Похоже, что лучшая документация для этой функции может быть найдена в стандартном предложении С++, написанном автором 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();  
}

Ответ 2

Благодаря @PSIAlt и @free_coffee я знаю, как использовать функции обратного вызова в stackful coroutine.

Вот простой пример для новичков asio (например, me: 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;
}

Ответ 3


Огромное спасибо 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) );