thread、coroutineの 例外を転送する！！

#例外の転送とは

C++を含む多くの言語では、子スレッドの例外を親が拾う事は出来ない
スレッド毎に例外のコンテキストが別なので、他のスレッドで例外を拾うには
例外を転送しなければならない


```cpp:子スレッドの例外は親でcatch出来ない
#include <iostream>
#include <thread>
#include <exception>


int main(){

  try{
    std::thread t([](){
      try{
        throw std::exception();    
      }catch(...){
        std::cout << "catch child" << std::endl;
        throw;
      }
    });
        
        
    t.join();
        
    }catch(...){
        std::cout << "catch parent" << std::endl;
    }
    
    return 1;
}
```

catch child
terminate called after throwing an instance of 'std::exception'

親で例外をハンドル出来ないので実行時エラーで落ちる。


#boost.asioのコルーチンでは？

boost.asioはコルーチンを使うことが出来る。軽量スレッド。とても優秀
しかし　このコルーチンも、例外を転送できなかった

```cpp:boost.asioのコルーチン
#include <iostream>
#include <exception>

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/bind.hpp>

using namespace std;
using namespace boost::asio::ip;

int main(){
    boost::asio::io_service io_service;
    
        try {
            boost::asio::spawn( io_service, [](boost::asio::yield_context yield ) {

                try {
                    throw std::exception();
                } catch (...) {
                    cout << "catch child" << endl;
                    throw;
                }
            });

        } catch (...) {
            cout << "catch parent" << endl;
        }
    
    
        boost::asio::signal_set signals(io_service);
        signals.add(SIGINT);
        signals.add(SIGTERM);
#if defined(SIGQUIT)
        signals.add(SIGQUIT);
#endif // defined(SIGQUIT)
        signals.async_wait(boost::bind(
                &boost::asio::io_service::stop, &io_service));

        io_service.run();
    
    return 1;
}

```

spawnで、コンテキスト yieldを作成し、ラムダ関数でコルーチン実行している
下のspawn以下は　この際無視して大丈夫

上のspawnで、ラムダ内で例外を発生させ、spawnの前後でcatchしようとしているが
これも上記と同じく、子供の例外を親が受け取れない

これは恐らく、コルーチンの実装もスレッドと同じく、例外コンテキストが個別になっているからだと思われる

#例外を転送する（thread）

C++で例外を転送すには std:: exception_ptrを使います

```cpp:exception_ptr(thread)

#include <iostream>
#include <thread>
#include <exception>
#include <stdexcept>



int main(){

  std::exception_ptr ep;
    
  try{
    std::thread t([&ep](){
      try{
        throw std::exception();    
      }catch(...){
        std::cout << "catch child" << std::endl;
        ep = std::current_exception();
      }
    });
        
        
    t.join();
    if(ep){
      std::rethrow_exception(ep);
    }

  }catch(...){
    std::cout << "catch parent" << std::endl;
  }
    
    return 1;
}
```

このようにすると

catch child
catch parent

と、親に例外を転送できました！！


#boost.asio コルーチンで行う

やることは同じですが、asioは非同期に設計さていて、spawn後に関数抜けてしまうので
無理やりですが　確認のために　無限ループコルーチンを作り
そこで確認してみます。。。


```cpp:コルーチン
#include <iostream>
#include <exception>

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/bind.hpp>
#include <stdexcept>

using namespace std;
using namespace boost::asio::ip;

int main(){
  boost::asio::io_service io_service;
    
  std::exception_ptr ep;


    boost::asio::spawn( io_service, [&ep](boost::asio::yield_context yield ) {

        try {
            throw std::exception();
        } catch (...) {
            cout << "catch in" << endl;
            ep = std::current_exception();
        }
    });

    // 無限ループ
    boost::asio::spawn(io_service, [&ep,&io_service](boost::asio::yield_context yield) {

        try {
            boost::asio::deadline_timer timer(io_service);
            for (; ;) {
                timer.expires_from_now(boost::posix_time::millisec(100));
                timer.async_wait(yield);
                if(ep){
                  std::rethrow_exception(ep);
                }


            }
        }catch(...){
            std::cout << "catch other" << std::endl;
            return;
        }
    });

    
    boost::asio::signal_set signals(io_service);
    signals.add(SIGINT);
    signals.add(SIGTERM);
#if defined(SIGQUIT)
    signals.add(SIGQUIT);
#endif // defined(SIGQUIT)
    signals.async_wait(boost::bind(
            &boost::asio::io_service::stop, &io_service));

    io_service.run();
    
    return 1;
}
```

catch child
catch other

例外が転送できましたね。


非同期になると、親で受け取りにくくなるため
使いにくい気もしますが
一応これで例外を転送できます