2014-06-30 20 views
13

我试图用boost::asio::spawn协程重写一个项目。项目的某些部分无法更改。例如,存储协议库也使用boost::asio编写,但没有协程。boost :: asio :: spawn yield作为回调

问题是如何将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的回调。我想通过一个收益对象作为async_request_data的第二个参数,所以我可以以同步的方式使用它。

这怎么办?我认为这可能通过一些代理对象,可能使用基于asio_handler_invoke的方法。但是我很难看到如何做到这一点。

回答

12

貌似此功能的最好的文档可以在C中找到++由升压ASIO作者撰写标准提案:

N4045 – Library Foundations for Asynchronous Operations, Revision 2

见9.1它说:

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.

我想你的情况下,CompletionToken模板参数实际上是boost::asio::yield_contexthandler_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(); 
} 
+0

当然,我需要一些代理对象,把它作为回调,但其并不清楚怎么写这个对象的胆量。 'yield_context'没有'operator()'(othewise它会按原样“没有代理”)。它有一些这里描述的胆量http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/basic_yield_context/basic_yield_context.html,但不清楚如何将它们结合起来做一个正确的协程简历。 – PSIAlt

+0

我觉得** handler **是**代理对象。请参阅编辑。 –

+0

它适用于一些小的变化!感谢指导! (我在另一个答案中发布了最终代码) – PSIAlt

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

太棒了!尽管如此,使用'detail'类并没有很好的气味。也许你可以从你尝试使用'handler_type'时发布错误? –

+0

@free_coffee错误:无法转换'result.boost :: asio :: async_result :: get >()'from'boost :: asio :: async_result >>: :type {aka void}'to'RequestResult '' – PSIAlt

+0

看起来'yield_context'在某种程度上被作为'async_result'模板参数传递,意味着'decltype(handler)'不是它应该是的。你能发布代码吗? –

6

感谢@PSIAlt和@free_coffee我知道如何在stackful协程使用回调函数。

这里只是对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; 
} 
+4

不错的小例子!有一个微妙的问题。必须通过['asio_handler_invoke()'](http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/asio_handler_invoke.html)钩子调用'handler_'以正确地与协程同步。否则,在多线程环境中,可能会发生竞争条件,在协程已经放弃之前尝试恢复协程。 'asio_handler_invoke'挂钩针对特定的类型被重载,因此不能对'std :: function <>'进行擦除。 [Here](http://coliru.stacked-crooked.com/a/c48d1dcf4608e967)是一个更新的解决方案。 –