2017-10-20 4 views
2

最後の引数boost :: asio :: yield_contextをとる非同期関数を作成したいと思います。例えば:error_codeをasio :: yield_contextに設定するには

int async_meaning_of_life(asio::yield_context yield); 

私はまた、ASIOはエラーコードを返す方法と矛盾しないようにしたいと思います。ユーザーがない場合はそれは、次のとおりです。

int result = async_meaning_of_life(yield); 

と機能に障害が発生し、それはsystem_error例外がスローされます。しかし、ユーザーがない場合:

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

その後 - 代わりに投げるの - エラーがecで返されます。

問題は、関数を実装するときに、演算子[]が使用されているかどうかをチェックし、そうであればそれを設定する明確な方法が見つからないようです。私たちはこのような何かを思い付いた:

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(ドキュメントだけではあるが)であるので、それは、ハックです。

私がこれを行うと考えることのできるもう1つの方法は、yieldオブジェクトをasio::handler_typeに変換して実行することです。しかし、この解決法は最高の状態では厄介なようです。

別の方法がありますか?

答えて

2

ASIOはここでパターンがどのようになるのです透過的

interfaces.¹そのAPIにuse_futureyield_contextまたは完了ハンドラを提供するために、async_resultを使用しています。

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

ありがとうございますが、これは「yield」オブジェクトを「asio :: handler」に変換したものです。質問。私はハンドラ、先物、コルーチンをサポートする汎用の非同期関数を作成しようとしていません。正反対ですが、コードの明快さのために可能な限り長い間、コルーチンを使用しようとしています。 –

+0

あなたの恐怖の確認としてこの答えを考えてみましょうか?あなたがしたいことは、誰かが新しい情報を得るまで(あるいはライブラリAPIが拡張されるまで)厄介な気持ちになることです。 – sehe

+0

遅れて申し訳ありません。私はあなたの答えを変更して、最初はどこかに明示的に言及することができないと思っています。誰かが間違っていることを証明するまでは、答えとしてマークするのが喜ばしいでしょう。 –

関連する問題