【boost.asio】streambufferからstd::stringへコピー【coroutine】

#はじめに
boost.asioでデータを取得する際に
strambufferを使って取得する事があります
取得したデータをstringに保存したい時ありますよね
（例えば jsonをデコードする等）
そんな時に役立つTips


##全体像
全体のコードです

とりあえず http GETし、contents取得します

下記のasioサンプルコードをベースに。
[asio sample sync]("http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/example/cpp03/http/client/sync_client.cpp")

# 同期
まず　ストリームをそのまま 標準出力へ表示
なんともない処理ですね
上記のコードの一部を抜粋

```cpp:sync_stream
// Write whatever content we already have to output.
if (response.size() > 0)
  std::cout << &response;

// Read until EOF, writing data to output as we go.
boost::system::error_code error;
while (boost::asio::read(socket, response,
    boost::asio::transfer_at_least(1), error))
  std::cout << &response;

```

responseは boost::asio::streambuf型です
streamなので coutに流す事が出来ます。

ではこれを std::string に送る方法です


## istreamを使う

[boost stream buff]("http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/reference/streambuf.html")

boostのサンプルでは上記のように istreamを使って stringに流しています

```cpp:istream
boost::asio::streambuf b;

// reserve 512 bytes in output sequence
boost::asio::streambuf::mutable_buffers_type bufs = b.prepare(512);

size_t n = sock.receive(bufs);

// received data is "committed" from output sequence to input sequence
b.commit(n);

std::istream is(&b);
std::string s;
is >> s;
```

同じように streambufを istream等を使い stringに流し込む事は可能ですが
空白文字が消えるなど、データ次第ではstream由来の問題が発生するので
今回は　この方法はNGです

##buffer_cast

[faithandbrave]("http://faithandbrave.hateblo.jp/entry/20110324/1300950590")

上記の方法。streambuf.data()でmutable_buffer or const_buffer を取得し
buffer_cast を使う

この方法で、streamを使わずにstringにデータを流し込めます。
ただし、私が試したところ、ゴミデータまで取得されたので
substrで、正常データまでで切りました

```cpp:buffer_cast
boost::system::error_code error;
std::string result = "";
while (boost::asio::read(socket, response,
        boost::asio::transfer_at_least(1), error)) {

  std::string tmp = boost::asio::buffer_cast<const char *>(response.data());
  result += tmp.substr(0, response.size());
}

```

response.size()　で実際に取得したデータサイズがわかるので
その長さで切り取りコピーします
result に受信データが全部入ったはず


#非同期

asioのサンプルは下記
[asio sample async]("http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/example/cpp03/http/client/async_client.cpp")

基本的に connect、read、write関数に async_をつけ、ハンドラを登録して
コールバック駆動にすれば簡単に動きますが

read()は、同期の場合は戻り値がtrueの場合はまだデータが存在していたけど
非同期のため、戻り値は使えません
かわりに、boost::system::error_code を使います

通常はsucceeded=0 ですが、eofやエラー時は !=0 になるので
if(!err) で eofの場合は処理が終了します。

出力すべき文字列を キャプチャして buffer_cast を使えば
バッファ全てを取得できますね

```cpp:async
  void handle_read_content(const boost::system::error_code& err)
  {
    if (!err)
    {
      // Write all of the data that has been read so far.
      std::cout << &response_;

      // Continue reading remaining data until EOF.
      boost::asio::async_read(socket_, response_,
          boost::asio::transfer_at_least(1),
          boost::bind(&client::handle_read_content, this,
            boost::asio::placeholders::error));
    }
    else if (err != boost::asio::error::eof)
    {
      std::cout << "Error: " << err << "\n";
    }
  }
```


## coroutine
非同期で書くとかなり面倒です。
そこで corouineを使うと　非常に簡単になります


```cpp:coroutine
boost::system::error_code ec;
std::string result;

while (!ec){
  boost::asio::async_read(socket, response,
    boost::asio::transfer_at_least(1), yield[ec]);

  std::string tmp = boost::asio::buffer_cast<const char *>(response.data());
  result += tmp.substr(0, response.size());
}
```


coroutine 便利！