2014-06-30 14 views
13

Ich versuche, ein Projekt mit boost::asio::spawn Koroutinen umzuschreiben. Einige Teile des Projekts können nicht geändert werden. Zum Beispiel wird die Speicherprotokollbibliothek auch mit boost::asio geschrieben, aber ohne Coroutinen.boost :: asio :: spawn Ausbeute als Rückruf

Das Problem ist, wie yield_context in einen normalen Callback (ein boost::function Objekt oder einen klassischen Funktor) konvertieren.

Dies ist, was wir in der Speicherbibliothek API haben:

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

ein boost::asio::yield_context In diesem Fall:

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

Wie wir aus den Beispielen kennen, kann der Asio Ausbeute Kontext wie folgt verwendet werden Objekt dient als Rückruf für async_read_some. Ich würde gerne ein Ergebnis Objekt als zweites Argument zu async_request_data übergeben, so dass ich es in einer synchronen Weise verwenden kann.

Wie kann das gemacht werden? Ich denke, dass es möglich sein kann über ein Proxy-Objekt, möglicherweise unter Verwendung eines Ansatzes basierend auf asio_handler_invoke. Aber ich habe Schwierigkeiten zu sehen, wie das geht.

Antwort

12

Sieht aus wie die beste Dokumentation für diese Funktion kann durch den Boost Asio Autor geschrieben in C++ Standardvorschlag zu finden:

N4045 – Library Foundations for Asynchronous Operations, Revision 2

Siehe Abschnitt 9.1, der sagt:

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

3: The completion token is converted into a handler, i.e. a function object to be called when the asynchronous operation completes. The signature specifies the arguments that will be passed to the handler.

Ich denke, in Ihrem Fall wird das CompletionToken Vorlage Argument tatsächlich boost::asio::yield_context undseinkonvertiert es in ein Callback-Objekt.


Hier ist der Code aus dem Abschnitt 9.1 aktualisiert Ihre async_request_data Funktion aufzurufen:

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

Sicher, ich brauche einig Proxy-Objekt, um es als Rückruf passieren, aber es ist nicht klar, wie man schreibt der Mut dieses Objekts. 'yield_context' hat kein' operator() '(sonst würde es" wie es ist "ohne Proxy funktionieren). Es hat einige Eingeweide, die hier beschrieben werden http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/basic_yield_context/basic_yield_context.html, aber es ist nicht klar, wie man sie kombiniert, um eine korrekte Coroutine-Resume zu machen . – PSIAlt

+0

Ich denke 'Handler' ** ist ** das Proxy-Objekt. Bitte siehe bearbeiten. –

+0

Es hat mit ein paar kleinen Änderungen funktioniert! Danke für die Wegbeschreibung! (Ich schrieb den endgültigen Code in einer anderen Antwort) – PSIAlt

3

Groß dank free_coffe i dies funktioniert verwaltet. Buchungslösung für meinen Fall, möglicherweise jemand braucht es.

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

Später können wir diesen Proxy verwenden:

RequestResult res = async_foo(pkt, std::forward<boost::asio::yield_context>(yield)); 
+0

Großartig! Riecht jedoch nicht richtig mit einer 'Detail' Klasse. Vielleicht können Sie die Fehler von dem Versuch, 'handler_type' zu ​​verwenden, posten? –

+0

@free_coffee 'Fehler: konnte 'result.boost :: asio :: async_result nicht konvertieren :: get >>() 'von' boost :: asio :: async_result >>: : tippe {aka void} 'in' RequestResult ' '' – PSIAlt

+0

Es sieht so aus, als ob der' yield_context' irgendwie als Template-Parameter 'async_result' übergeben wird, was bedeutet, dass' declltype (handler) 'nicht so ist, wie er sein sollte. Könnten Sie den Code posten? –

6

Dank @PSIAlt und @free_coffee Ich weiß, wie in stackful Koroutine Callback-Funktionen zu verwenden.

Hier ist ein einfaches Beispiel nur für Asio Neulinge (wie ich: 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; 
} 
+4

Nettes minimales Beispiel! Es gibt ein subtiles Problem. 'handler_' muss über den [' asio_handler_invoke() '] (http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/asio_handler_invoke.html) Hook aufgerufen werden, um eine korrekte Synchronisation mit der Coroutine zu erreichen . Andernfalls kann in einer Multithread-Umgebung eine Race-Bedingung auftreten, wenn versucht wird, die Coroutine wieder aufzunehmen, bevor sie nachgegeben hat. Der 'asio_handler_invoke'-Hook ist für bestimmte Typen überladen, so dass das Löschen von' std :: function <> 'nicht auftreten kann. [Hier] (http://coliru.stacked-crooked.com/a/c48d1dcf4608e967) ist eine aktualisierte Lösung. –