2017-10-20 141 views
2

我想创建一个异步函数,它将最后一个参数boost :: asio :: yield_context作为参数。例如: -如何将error_code设置为asio :: yield_context

int async_meaning_of_life(asio::yield_context yield); 

我也想与短耳如何返回错误代码是一致的。也就是说,如果用户确实:

int result = async_meaning_of_life(yield); 

和函数调用失败,则抛出system_error例外。但是,如果用户确实:

boost::error_code ec; 
int result = async_meaning_of_life(yield[ec]); 

然后 - 而不是抛出 - 在ec返回错误。

问题是,当实现该功能时,我似乎无法找到一个干净的方法来检查是否使用了operator [],并且如果是这样的话就设置它。我们想出了这样的事情:

inline void set_error(asio::yield_context yield, sys::error_code ec) 
{ 
    if (!yield.ec_) throw system_error(ec); 
    *(yield.ec_) = ec; 
} 

但是,这是哈克,因为yield_context::ec_declared private(虽然只在文档中)。

我能想到的另一种方法是将yield对象转换为asio::handler_type并执行它。但是这个解决方案看起来好像很尴尬。

还有别的办法吗?

回答

2

短耳使用async_result透明地提供use_futureyield_context或完成处理的API中interfaces.¹

这里的模式如何去:

template <typename Token> 
auto async_meaning_of_life(bool success, Token&& token) 
{ 
    typename asio::handler_type<Token, void(error_code, int)>::type 
       handler (std::forward<Token> (token)); 

    asio::async_result<decltype (handler)> result (handler); 

    if (success) 
     handler(42); 
    else 
     handler(asio::error::operation_aborted, 0); 

    return result.get(); 
} 

更新

与提升1起。66,用于标准化的图案adheres to the interface proposed

using result_type = typename asio::async_result<std::decay_t<Token>, void(error_code, int)>; 
    typename result_type::completion_handler_type handler(std::forward<Token>(token)); 

    result_type result(handler); 

综合演示

显示如何与

  • 科罗的和产率[EC]
  • 科罗的和产量+例外
  • 使用它
  • std :: future
  • 个完成处理程序

Live On Coliru

#define BOOST_COROUTINES_NO_DEPRECATION_WARNING 
#include <iostream> 
#include <boost/asio.hpp> 
#include <boost/asio/spawn.hpp> 
#include <boost/asio/use_future.hpp> 

using boost::system::error_code; 
namespace asio = boost::asio; 

template <typename Token> 
auto async_meaning_of_life(bool success, Token&& token) 
{ 
#if BOOST_VERSION >= 106600 
    using result_type = typename asio::async_result<std::decay_t<Token>, void(error_code, int)>; 
    typename result_type::completion_handler_type handler(std::forward<Token>(token)); 

    result_type result(handler); 
#else 
    typename asio::handler_type<Token, void(error_code, int)>::type 
       handler(std::forward<Token>(token)); 

    asio::async_result<decltype (handler)> result (handler); 
#endif 

    if (success) 
     handler(error_code{}, 42); 
    else 
     handler(asio::error::operation_aborted, 0); 

    return result.get(); 
} 

void using_yield_ec(asio::yield_context yield) { 
    for (bool success : { true, false }) { 
     boost::system::error_code ec; 
     auto answer = async_meaning_of_life(success, yield[ec]); 
     std::cout << __FUNCTION__ << ": Result: " << ec.message() << "\n"; 
     std::cout << __FUNCTION__ << ": Answer: " << answer << "\n"; 
    } 
} 

void using_yield_catch(asio::yield_context yield) { 
    for (bool success : { true, false }) 
    try { 
     auto answer = async_meaning_of_life(success, yield); 
     std::cout << __FUNCTION__ << ": Answer: " << answer << "\n"; 
    } catch(boost::system::system_error const& e) { 
     std::cout << __FUNCTION__ << ": Caught: " << e.code().message() << "\n"; 
    } 
} 

void using_future() { 
    for (bool success : { true, false }) 
    try { 
     auto answer = async_meaning_of_life(success, asio::use_future); 
     std::cout << __FUNCTION__ << ": Answer: " << answer.get() << "\n"; 
    } catch(boost::system::system_error const& e) { 
     std::cout << __FUNCTION__ << ": Caught: " << e.code().message() << "\n"; 
    } 
} 

void using_handler() { 
    for (bool success : { true, false }) 
     async_meaning_of_life(success, [](error_code ec, int answer) { 
      std::cout << "using_handler: Result: " << ec.message() << "\n"; 
      std::cout << "using_handler: Answer: " << answer << "\n"; 
     }); 
} 

int main() { 
    asio::io_service svc; 

    spawn(svc, using_yield_ec); 
    spawn(svc, using_yield_catch); 
    std::thread work([] { 
      using_future(); 
      using_handler(); 
     }); 

    svc.run(); 
    work.join(); 
} 

打印:

using_yield_ec: Result: Success 
using_yield_ec: Answer: 42 
using_yield_ec: Result: Operation canceled 
using_yield_ec: Answer: 0 
using_future: Answer: 42 
using_yield_catch: Answer: 42 
using_yield_catch: Caught: Operation canceled 
using_future: Caught: Operation canceled 
using_handler: Result: Success 
using_handler: Answer: 42 
using_handler: Result: Operation canceled 
using_handler: Answer: 0 

注意:为了简单起见,我还没有添加输出同步,因此输出可以根据成为混在运行时执行订单


¹参见例如,这个优秀的演示如何使用它来扩展你自己的异步结果模式的库boost::asio with boost::unique_future

+0

谢谢@sehe,但这是“将yield对象转换为'asio :: handler'”解决方案,我在这个问题。我没有试图创建一个支持处理程序,期货和协同程序的通用异步函数。恰恰相反,为了代码清晰,我试图尽可能长时间地使用协程。 –

+0

想想这个答案是为了确认你的恐惧吗?你希望做的事情会一直呆在尴尬的地方,直到有人出现新的信息(或者API库被扩展)。 – sehe

+0

抱歉,延迟。我认为如果你修改你的答案,明确提到某个地方在目前是不可能的,我会很乐意将它标记为答案,直到有人证明我们错了。 –