boost::asio::générer des rendements en rappel

j'essaie de réécrire un projet en utilisant boost::asio::spawn coroutines. Certaines parties du projet ne peuvent pas être modifiées. Par exemple, la bibliothèque de protocole de stockage est aussi écrite avec boost::asio, mais sans coroutines.

le problème est de savoir comment convertir yield_context dans un rappel normal (a boost::function objet ou un classique foncteur).

C'est ce que nous avons dans L'API de bibliothèque de stockage:

void async_request_data(uint64_t item_id, boost::function< void(Request_result *) > callback);

comme nous le savons par des exemples, le contexte de rendement asio peut être utilisé comme ceci:

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

Dans ce cas, un boost::asio::yield_context objet sert comme un rappel pour async_read_some. Je tiens à passer un rendement object comme second argument à async_request_data, donc je peux l'utiliser de façon synchrone.

Comment cela peut-il être fait? Je pense que cela peut être possible via un proxy-objet, peut-être en utilisant une approche basée sur asio_handler_invoke. Mais j'ai du mal à voir comment faire cela.

13
demandé sur paisanco 2014-06-30 23:49:20

3 réponses

Regarde comme la meilleure documentation pour cette fonctionnalité peut être trouvée dans une norme C++ proposition écrite par le coup de pouce asio auteur:

N4045 – Bibliothèque des Fondations pour les Opérations Asynchrones, Révision 2

Voir la section 9.1, qui dit:

handler_type_t<CompletionToken, void(error_code, size_t)>   #3
  handler(std::forward<CompletionToken>(token));

3: le token completion est converti en handler, c'est-à-dire un objet de fonction à appeler lorsque l'opération asynchrone est terminée. La signature spécifie les arguments que sera transmis au gestionnaire.

je suppose que dans votre cas, le CompletionToken l'argument template sera en fait boost::asio::yield_context et handler_type convertit un objet de rappel.


voici le code de la section 9.1 mis à jour pour appeler votre async_request_data fonction:

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();  
}
13
répondu free_coffee 2014-07-09 12:31:38

grâce à @PSIAlt et @free_coffee je sais comment utiliser les fonctions de rappel dans stackful coroutine.

voici un exemple simple juste pour les débutants asio (comme moi: 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;
}
6
répondu cfy 2016-11-22 21:49:40

un Grand merci free_coffe j'ai réussi ce travail. Solution d'affichage pour mon cas, peut-être que quelqu'un en a besoin.

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();
}

plus Tard, nous pouvons utiliser ce proxy:

RequestResult res = async_foo(pkt, std::forward<boost::asio::yield_context>(yield) );
3
répondu PSIAlt 2014-07-12 15:59:08