On this page
article
C++ Coroutine
Generator 예제와 구현 원리
C++20에서는 코루틴(Coroutine) 기능이 표준에 포함되어 비동기 로직이나 상태를 유지하는 반복 처리를 더욱 직관적으로 표현할 수 있게 되었다. 이 문서에서는 Generator
스타일의 코루틴을 예제로 설명하며, promise_type
, co_await
, std::coroutine_handle
의 개념과 구현 방식을 정리한다.
기본 예제
#include <iostream>
#include <coroutine>
using namespace std;
struct Generator {
struct Promise {
int value;
Generator get_return_object() {
return Generator{ std::coroutine_handle<Promise>::from_promise(*this) };
}
auto initial_suspend() { return std::suspend_always{}; }
auto yield_value(int x) {
value = x;
return std::suspend_always{};
}
auto return_void() { return std::suspend_never{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() { std::exit(1); }
};
using promise_type = Promise;
std::coroutine_handle<Promise> coro;
Generator(std::coroutine_handle<Promise> h) : coro(h) {}
~Generator() { if (coro) coro.destroy(); }
int value() const { return coro.promise().value; }
bool next() { coro.resume(); return !coro.done(); }
};
Generator foo(int n) {
std::cout << "\tRun 1 : " << std::endl;
co_await std::suspend_always{}; // suspend after Run 1
std::cout << "\tRun 2" << std::endl;
co_await std::suspend_always{};
std::cout << "\tRun 3" << std::endl;
}
int main() {
Generator f = foo(10);
std::cout << "main 1 : " << std::endl;
f.next();
std::cout << "main 2" << std::endl;
f.next();
std::cout << "main 3" << std::endl;
f.next();
}
Coroutine 구현 요점
1. co_await std::suspend_always{}
- 항상 suspend 되므로 caller로 제어권을 넘김
- 일종의 Awaitable 구조체
2. 반환 타입 규칙 (Generator)
C++의 코루틴 함수는 특정한 반환 타입 규칙을 따라야 한다. 아래 항목이 반드시 구현되어야 한다.
내부에
promise_type
이 정의되어 있어야 함std::coroutine_handle<promise_type>
를 멤버로 가져야 함단일 인자를 받는 생성자
소멸자에서 coroutine 파괴 수행
promise_type
내부에 다음 5개 필수 메서드:get_return_object()
initial_suspend()
final_suspend()
return_void()
또는return_value()
unhandled_exception()
최소 구현 예시
#include <iostream>
#include <coroutine>
class Generator {
public:
struct Promise {
Generator get_return_object() {
return Generator{ std::coroutine_handle<Promise>::from_promise(*this) };
}
auto initial_suspend() { return std::suspend_always{}; }
auto return_void() { return std::suspend_never{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() { std::exit(1); }
};
using promise_type = Promise;
std::coroutine_handle<Promise> coro;
Generator(std::coroutine_handle<Promise> c) : coro(c) {}
~Generator() { if (coro) coro.destroy(); }
};
항목 | 설명 |
---|---|
co_await std::suspend_always{} | 무조건 suspend. 제어권을 caller에게 넘김 |
promise_type | 코루틴 동작을 관리하는 핵심 클래스 |
coroutine_handle | 실행 상태를 제어할 수 있는 핸들 |
yield_value() | 값을 반환하고 suspend |
return_void() | 반환 없이 종료 |
final_suspend() | 마지막 suspend 시점에서 caller로 복귀 |