c++20 の coroutine 使ってみる

vc2022 で実験。

実験コードはこちら。
https://github.com/ousttrue/co_sample

https://cpprefjp.github.io/lang/cpp20/coroutines.html

https://cpprefjp.github.io/reference/coroutine/coroutine_handle.html

# 最小限
とりあえず compile を通して必要最小限を調べる

```c++
#include <coroutine>
#include <functional>
#include <iostream>

// Promise
struct Co {
  struct promise_type // 👈重要
  {
    auto get_return_object() {
      return Co{std::coroutine_handle<promise_type>::from_promise(*this)}; // 👈重要
    }
    auto initial_suspend() {
      std::cout << "[promise_type] initial_suspend" << std::endl;
      return std::suspend_always{};
    }
    auto final_suspend() noexcept {
      std::cout << "[promise_type] final_suspend" << std::endl;
      return std::suspend_always{};
    }
    void unhandled_exception() { std::terminate(); }
    void return_void() {}
  };
  std::coroutine_handle<promise_type> handle; // 👈重要
};

// coroutine
Co run() {
  std::cout << "[run] before" << std::endl;
  co_return;
  std::cout << "[run] after" << std::endl;
}

int main(int argc, char **argv) {
  auto co = run();
  std::cout << "[main]before resume: " << co.handle.done() << std::endl;
  co.handle.resume(); // 👈重要
  std::cout << "[main]after resume: " << co.handle.done() << std::endl;
  return 0;
}
```

実行すると

```
$ ./prefix/bin/simple.exe
[promise_type] initial_suspend
[main]before resume: 0
[run] before
[promise_type] final_suspend
[main]after resume: 1
```

というふうになります。

:::note
resume() で進行する必要あり。
`C#` や `lua` と同じ感じ。
:::

:::note warn
ビルドに `/std:c++latest` が必要。
`/std:C++20` でもよいかもしれない。
:::

## c++ の coroutine
関数 body で `co_return`, `co_await`, `co_yield` のいずれかを使う関数。
その返り値 `R` 型は、`R::promise_type` 型を持っている必要がある。

```c++
struct R
{
  struct promise_type
  {
    // 省略
  };
};
R run() {
  co_return; // co_return などが返り値の R::primise_type を要求する
}
```

:::note
要求を満たしていないと普通に関数がないコンパイルエラーになる。
:::

### promise_type

5つの member 関数が必要。

```c++
struct R {
  struct promise_type {
    auto get_return_object();
    auto initial_suspend();
    auto final_suspend() noexcept;
    void unhandled_exception();
    void return_void();
  };
};
```

:::note
要求を満たしていないと普通に関数がないコンパイルエラーになる。
:::

### std::coroutine_handle<promise_type>
コルーチンを再開(resume())したり、コルーチンが終了済み(done())か調べたりする。

`R` のメンバーに入れて `get_return_object` で初期化するのがパターン。

```c++
struct R {
  struct promise_type
  {
    auto get_return_object() {
      return R{
        .handle = std::coroutine_handle<promise_type>::from_promise(*this)
      };
    }
    // 他は省略
  };
  std::coroutine_handle<promise_type> handle; // 👈 これ
};
```

:::note
R のメンバーにしないと resume() する手段が無い？
:::

# await (suspend) してみよう

```c++
#include <coroutine>
#include <functional>
#include <iostream>
#include <thread>

struct Awaiter
{
  bool await_ready()
  {
    std::cout << "[Awaiter] await_ready" << std::endl;
    // false ならば suspend する。
    return false;
  }
  void await_suspend(std::coroutine_handle<> handle)
  {
    std::cout << "[Awaiter] await_suspend" << std::endl;
  }
  void await_resume() { std::cout << "[Awaiter] await_resume" << std::endl; }
};

// Promise
struct Co
{
  struct promise_type
  {
    auto get_return_object()
    {
      return Co{ std::coroutine_handle<promise_type>::from_promise(*this) };
    }
    auto initial_suspend()
    {
      std::cout << "[promise_type] initial_suspend" << std::endl;
      return std::suspend_always{};
    }
    auto final_suspend() noexcept
    {
      std::cout << "[promise_type] final_suspend" << std::endl;
      return std::suspend_always{};
    }
    void unhandled_exception() { std::terminate(); }
    void return_void()
    {
      std::cout << "[promise_type] return_void" << std::endl;
    }
  };
  std::coroutine_handle<promise_type> handle;
};

// coroutine
Co
run()
{
  std::cout << "[run] before" << std::endl;
  co_await Awaiter{};
  std::cout << "[run] after" << std::endl;
  // co_return;
}

int
main(int argc, char** argv)
{
  auto co = run();
  for (int i = 0; !co.handle.done(); ++i) {
    std::cout << "[main]resume " << i << std::endl;
    co.handle.resume();
  }
  return 0;
}
```

```
[promise_type] initial_suspend
[main]resume 0
[run] before
[Awaiter] await_ready
[Awaiter] await_suspend
[main]resume 1
[Awaiter] await_resume
[run] after
[promise_type] return_void
[promise_type] final_suspend
```

# Awaiter をユーザーから隠して間接的に生成させる
上記の例では下記のように直接 Awaiter を作りました。

```c++
  co_await Awaiter{};
```

本来の使い方では、Awaiter を間接的に生成して隠蔽するデザインになっているようです。
２通りの手段があります。

* Awaiter operator co_await(const T &)
* Awaiter promise_type::await_transform(const T &)

:::note
Awaiter と Awaitable の違いが不明瞭。
operator co_await が Awaiter で、
await_transform が Awaitable と言っているようにみえた。
:::

:::note
時間から Awaiter を生成する例。
```c++
co_await 10ms;
```

```c++
// Awaiter に変換する

Awaiter operator co_await(const chrono::milliseconds &)

// もしくは

Awaiter promise_type::await_transform(const chrono::milliseconds &)
```

:::

:::note

```c++
    auto initial_suspend() {
      std::cout << "[promise_type] initial_suspend" << std::endl;
      return std::suspend_always{}; // 👈 awaiter
    }
    auto final_suspend() noexcept {
      std::cout << "[promise_type] final_suspend" << std::endl;
      return std::suspend_always{}; // 👈 awaiter
    }
```

https://cpprefjp.github.io/reference/coroutine/suspend_always.html

:::

# co_yield: 中断(suspend)するときに coroutine から呼び出し側に値を返す

```c++
#include <coroutine>
#include <functional>
#include <iostream>
#include <thread>

struct Awaiter
{
  bool await_ready()
  {
    std::cout << "[Awaiter] await_ready" << std::endl;
    return false;
  }
  void await_suspend(std::coroutine_handle<> handle)
  {
    std::cout << "[Awaiter] await_suspend" << std::endl;
  }
  void await_resume() { std::cout << "[Awaiter] await_resume" << std::endl; }
};

// Promise
struct Co
{
  struct promise_type
  {
    auto get_return_object()
    {
      return Co{ std::coroutine_handle<promise_type>::from_promise(*this) };
    }
    auto initial_suspend()
    {
      std::cout << "[promise_type] initial_suspend" << std::endl;
      return std::suspend_always{};
    }
    auto final_suspend() noexcept
    {
      std::cout << "[promise_type] final_suspend" << std::endl;
      return std::suspend_always{};
    }
    void unhandled_exception() { std::terminate(); }
    void return_void()
    {
      std::cout << "[promise_type] return_void" << std::endl;
    }

    int m_value = -1;
#if 1
    std::suspend_always yield_value(int value) // 👈 co_yield 時に呼ばれる
    {
      m_value = value;
      return {};
    }
#else
    std::suspend_always await_transform(int value) // co_await で書けばこう？
    {
      m_value = value;
      return {};
    }
#endif
  };
  std::coroutine_handle<promise_type> handle;
};

// coroutine
Co
run()
{
  std::cout << "[run] before" << std::endl;
  co_yield 1;
  co_yield 2;
  co_yield 3;
  std::cout << "[run] after" << std::endl;
  // co_return;
}

int
main(int argc, char** argv)
{
  auto co = run();
  for (int i = 0; !co.handle.done(); ++i) {
    std::cout << "[main]resume " << i << "=>" << co.handle.promise().m_value // 👈 m_value が更新される
              << std::endl;
    co.handle.resume();
  }
  return 0;
}
```

```
[promise_type] initial_suspend
[main]resume 0=>-1
[run] before
[main]resume 1=>1
[main]resume 2=>2
[main]resume 3=>3
[run] after
[promise_type] return_void
[promise_type] final_suspend
```

# co_return で値を返す

```c++
#include <coroutine>
#include <functional>
#include <iostream>
#include <thread>

// Promise
template<typename T>
struct Co
{
  struct promise_type
  {
    auto get_return_object()
    {
      return Co{ std::coroutine_handle<promise_type>::from_promise(*this) };
    }
    auto initial_suspend()
    {
      std::cout << "[promise_type] initial_suspend" << std::endl;
      return std::suspend_always{};
    }
    auto final_suspend() noexcept
    {
      std::cout << "[promise_type] final_suspend" << std::endl;
      return std::suspend_always{};
    }
    void unhandled_exception() { std::terminate(); }

    T m_value = -1;
    void return_value(T value)
    {
      m_value = value;
      std::cout << "[promise_type] return_value" << std::endl;
    }
  };
  std::coroutine_handle<promise_type> handle;

  T Value()
  {
    return handle.promise().m_value;
  }
};

// coroutine
Co<int>
run()
{
  std::cout << "[run] before" << std::endl;
  co_return 345;
  std::cout << "[run] after" << std::endl;
}

int
main(int argc, char** argv)
{
  auto co = run();
  std::cout << "[main]before resume: " << co.handle.done() << " => "
            << co.Value() << std::endl;
  co.handle.resume();
  std::cout << "[main]after resume: " << co.handle.done() << " => "
            << co.Value() << std::endl;
  return 0;
}
```

```
[promise_type] initial_suspend
[main]before resume: 0 => -1
[run] before
[promise_type] return_value
[promise_type] final_suspend
[main]after resume: 1 => 345
```

# 再開(resume)するとき coroutine に値を渡す

Task ぽくするには、suspend 時に thread に処理を投げて、
thread 終了時に結果を resume に投入して再開する必要がありそう。

```c++
#include <chrono>
#include <coroutine>
#include <functional>
#include <future>
#include <iostream>
#include <thread>

// Promise
template<typename T>
struct Co
{
  struct promise_type
  {
    auto get_return_object()
    {
      return Co{ std::coroutine_handle<promise_type>::from_promise(*this) };
    }
    auto initial_suspend()
    {
      std::cout << "[promise_type] initial_suspend" << std::endl;
      return std::suspend_always{};
    }
    auto final_suspend() noexcept
    {
      std::cout << "[promise_type] final_suspend" << std::endl;
      return std::suspend_always{};
    }
    void unhandled_exception() { std::terminate(); }

    auto await_transform(const std::function<T()>& work)
    {
      struct awaiter
      {
        std::future<T> m_future;
        std::thread m_t;
        bool await_ready() { return false; }
        void await_suspend(std::coroutine_handle<>) {}
        T await_resume() // 👈 await_resume の返り値が co_await  の左辺値になる
        {
          std::cout << "thread block ? tid#" << std::this_thread::get_id()
                    << std::endl;
          return m_future.get();
        }
      };

      std::promise<T> p;
      std::future<T> f = p.get_future();
      std::thread t([work, p = std::move(p)]() mutable {
        std::cout << "[work]tid#" << std::this_thread::get_id() << std::endl;
        T value = work();   // 👈 thread 上で work を実行
        p.set_value(value); // 結果を返す
      });

      // join しないのでエラーにならなように
      t.detach();

      return awaiter{
        std::move(f),
        std::move(t),
      };
    }

    T m_value = -1;
    void return_value(T value)
    {
      m_value = value;
      std::cout << "[promise_type] return_value" << std::endl;
    }
  };
  std::coroutine_handle<promise_type> handle;

  T Value() { return handle.promise().m_value; }
};

// coroutine
Co<int>
run()
{
  std::cout << "[run] before" << std::endl;
  int result = co_await []() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 678;
  };
  std::cout << "[run] after" << std::endl;
  co_return result;
}

int
main(int argc, char** argv)
{
  auto co = run();
  while (!co.handle.done()) {
    co.handle.resume();
  }
  std::cout << "[main]" << co.Value() << std::endl;
  return 0;
}
```

```
[promise_type] initial_suspend
[run] before
thread block ? tid#24008
[work]tid#9152
[run] after
[promise_type] return_value
[promise_type] final_suspend
[main]678
```

実用するには、工夫して promise.set_value が完了してから resume してあげるようにする必要あり。

# boost asio の coroutine api を使う

asio の記事に移動しました。

https://qiita.com/ousttrue/items/2211559a9a552f23cef4

# 参考

https://qiita.com/tyanmahou/items/522ea1c592db3468940c
https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html
https://downloads.ctfassets.net/oxjq45e8ilak/4kVoTkxYBiVM9lPBZiG2HO/a5e36dc80fd898885269bd6320c96196/Pavel_Novikov_Uchimsya_gotovit_C_korutiny_na_praktike_2020_06_28_18_49_49.pdf