2017-10-20 93 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

抱歉,延遲。我認爲如果你修改你的答案,明確提到某個地方在目前是不可能的,我會很樂意將它標記爲答案,直到有人證明我們錯了。 –

相關問題