LoginSignup
5
4

More than 3 years have passed since last update.

C++のCoroutineちょっと使ってみた

Last updated at Posted at 2020-03-07

C++20でCoroutineが追加されますね。Coroutineについてはこちらを参照してください。私は東方が好きで東方弾幕風というやつを一時期やってまして、そこにも同様の機能がありました。それを、今時分が制作しているゲームにも組み込めないかなと。

想像と違った。

さっきのサイトの通り、C++で実装されるものは、弾幕風のマイクロスレッドのような便利なものではなく、それを実装するためのものでした。つまり、自分でマイクロスレッドを作るみたいな感じです。そして、弾幕風はメインスレッドに戻るのに対し、こちらは呼び出し元に戻るという違いがあります。

壁にあたった

それにしてもこのCoroutine。新しいものということもあってか、日本語の文献が全然ない。自分が知ってる限りで日本語でサンプルとかあるのは2箇所だけ。1
でもわかりやすくて助かりました。

できたもの

testyieldbig.gif
こんな感じに1文字ずつ描画されます(フレームレートとかの関係でそう見えないだけ)。DXライブラリです。

コード

ジェネレータは先述のサイトとほぼ一緒です。ただ、今回は返り値は必要ないのでとりあえず捨ててます。どうにかしたいな。
とりあえず動くようにしたので余計なこととかもありますのでそこは適宜。

task.h
#include <experimental/coroutine>
#define YIELD (co_yield NULL) /*とりあえずNULL返してる。どうにかしたい*/

struct task {
    bool flag = true;
    struct promise_type {
        auto get_return_object() { return task{ handle_type::from_promise(*this) }; }
        std::experimental::suspend_never initial_suspend()noexcept { return {}; }
        std::experimental::suspend_always final_suspend()noexcept { return {}; }
        std::experimental::suspend_always yield_value(void*)noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };
    using handle_type = std::experimental::coroutine_handle<promise_type>;
    explicit task(handle_type h)
        : coro_(h) {}
    task(const task&) = delete;
    task(task&& rhs)
        : coro_(rhs.coro_) { rhs.coro_ = nullptr; }
    ~task() {
        if (coro_)
            coro_.destroy();
    }
    void processing() {
        coro_.promise();
    }

    void update() {
        if (!flag) return;
        coro_.resume();
        flag = !coro_.done();
        processing();
    }

    handle_type coro_;
};

変更点としては、bool flagを追加したくらいです。これにより、終了した場合にタスクの呼び出しをしなくなります。ただ、今回の場合は必要ないです。ホントはprivateにするとかしたほうがいいと思う。

print.cpp
/*省略*/
task print(std::string str) {
    std::string b;
    while (1) {
        if (b != str) {
            for (auto c : str) {
                b.push_back(c);
                DrawString(0, 0, b.c_str(), 0x000000);
                YIELD;
            }
        }
        else {
            DrawString(0, 0, b.c_str(), 0x000000);
            YIELD;
        }
    }
    co_return;
}
main.cpp
/*インクルードとか省略*/
int WINAPI WinMain(/*引数いっぱい*/) {

/*省略*/

    auto DrawString = print("TestString", font);
    while(/*メインループの処理とか*/){
        DrawString.update();
    }

/*省略*/

}

使い方としては、戻り値taskの関数を使用して、中断するところでYIELDを書く。基本無限ループの中に突っ込めばいいと思う。一応終わりにはco_returnを書いとくほうがいいと思います。これで、1フレームごとに1文字描画するようになりました。

最後に

みんなもっとcoroutine使って。まだ実装されてなかったりするものもあるけど。MSVCはまだ実験だけど使えるから!動いたから!
そしてもっと日本語の増やして!正直わからんことだらけです。

ライセンス

ライセンスはCC0です。自由に使用、改変して結構です。
CC0

参考

C++コルーチン拡張メモ

C++ でコルーチン (async/await 準備編)

20分くらいでわかった気分になれるC++20コルーチン


  1. 「参考」の上2つ。 

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4