【C++】std::coroutineで出来ることと典型的な使い方【標準ライブラリ】

【C++】std::coroutineで出来ることと典型的な使い方【標準ライブラリ】

std::coroutineとは

C++20で導入されたコルーチンは、関数の実行を途中で一時停止し、後で再開する機能を提供します。従来の関数とは異なり、コルーチンは途中で状態を保存しながら実行を継続できます。これにより、非同期処理、ジェネレータ、協調的マルチタスク処理などが簡潔に記述できます。

コルーチンの基本構造

C++のコルーチンを使うには、以下の3つの要素が必要です。

  • Promise Type: コルーチンの状態管理を行うクラス
  • ハンドル(std::coroutine_handle): コルーチンを制御するオブジェクト
  • サスペンドポイント(suspend points): コルーチンを一時停止・再開する箇所

以下のコードは、基本的なコルーチンの構造を示しています。

#include <coroutine>
#include <iostream>

struct SimpleCoroutine {
    struct promise_type {
        SimpleCoroutine get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
    };
};

SimpleCoroutine my_coroutine() {
    std::cout << "Coroutine started" << std::endl;
    co_return;
}

int main() {
    my_coroutine();
    return 0;
}

Promise Typeとは

Promise Typeは、コルーチンのライフサイクルを管理する重要な要素です。このクラスは、以下の主要なメンバ関数を持ちます。

  • get_return_object(): コルーチンが返すオブジェクトを作成
  • initial_suspend(): コルーチンの開始時にサスペンドするか決定
  • final_suspend(): コルーチン終了時にサスペンドするか決定
  • return_value(T): コルーチンの戻り値をセット
  • unhandled_exception(): 例外処理

コルーチンの中断点

コルーチンには3つの主な中断点があります。

  • co_await: 非同期処理を待機
  • co_yield: 値を返しながら中断
  • co_return: 値を返して終了

ジェネレータの実装

ジェネレータは、値を逐次生成するコルーチンの一種です。

#include <coroutine>
#include <iostream>

template<typename T>
struct Generator {
    struct promise_type {
        T current_value;
        Generator get_return_object() { return Generator{this}; }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        std::suspend_always yield_value(T value) {
            current_value = value;
            return {};
        }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };

    std::coroutine_handle<promise_type> handle;
    Generator(promise_type* p) : handle(std::coroutine_handle<promise_type>::from_promise(*p)) {}
    ~Generator() { handle.destroy(); }
    bool next() { handle.resume(); return !handle.done(); }
    T value() { return handle.promise().current_value; }
};

Generator<int> count_to_five() {
    for (int i = 1; i <= 5; ++i) {
        co_yield i;
    }
}

int main() {
    auto gen = count_to_five();
    while (gen.next()) {
        std::cout << gen.value() << std::endl;
    }
}

非同期処理の実装

コルーチンを使うと、簡潔な非同期処理が可能です。

#include <coroutine>
#include <iostream>
#include <chrono>
#include <thread>

struct Task {
    struct promise_type {
        Task get_return_object() { return {}; }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };
};

Task async_function() {
    std::cout << "Task started" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Task completed" << std::endl;
    co_return;
}

int main() {
    async_function();
    std::cout << "Main continues" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 0;
}

コルーチンを使ったパイプライン処理

コルーチンを使うと、データ処理のパイプラインをシンプルに記述できます。

(省略: パイプライン処理の具体例を追加可能)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です