이번 강좌는 co_yield 이용하여 값을 생산하면서 제어권을 다른 쪽으로 넘기는 예제를 살펴보도록 하겠습니다. 코루틴 상태 관련 객체로 cotask_enumarator을 정의하여 사용합니다. 구체적으로 값을 생산하는 함수A 예를 살펴보도록 하겠습니다. 이 예제는 Unity 등 C#에서 많이 쓰는 IEnumerator를 리턴하는 함수와 같은 cotask_func_enum 함수를 정의하고 있습니다. 이 예제는 C#에서 많이 쓰는 코루틴 기법을 C++에서 사용하는 법을 보여주고 있습니다.
예제 코드는 아래와 같습니다.
#include <iostream>
#include <coroutine>
#include <concepts>
#include <exception>
#include <optional>
#include <vector>
using namespace std;
template<typename T>
struct cotask_enumarator {
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
struct promise_type {
std::optional<T> value_ = std::nullopt;
std::exception_ptr exception_;
cotask_enumarator get_return_object() {
return cotask_enumarator(handle_type::from_promise(*this));
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { exception_ = std::current_exception(); }
template<std::convertible_to<T> From>
std::suspend_always yield_value(From&& from) {
value_ = std::forward<From>(from);
return {};
}
void return_void() {}
};
handle_type handler_;
cotask_enumarator(handle_type handler) : handler_(handler) {}
~cotask_enumarator()
{
handler_.destroy();
}
bool done() {
return handler_.done();
}
std::optional<T> operator()() {
handler_();
if (handler_.promise().exception_)
std::rethrow_exception(handler_.promise().exception_);
auto ret = std::move(handler_.promise().value_);
handler_.promise().value_ = std::nullopt;
return ret;
}
};
cotask_enumarator<int> cotask_func_enum()
{
vector<int> items{ 1,2,3,4,5 };
for (auto item : items)
{
co_yield item;
}
}
int main(int argc, char* argv[])
{
auto cotask = cotask_func_enum();
while (!cotask.done())
{
auto value = cotask();
if (value.has_value())
{
cout << value.value() << endl;
}
}
return 0;
}
실행 결과는 아래와 같습니다.
1
2
3
4
5
예제 설명
cotask_func_enum() 함수에서 코루틴으로 값을 생산하고 호출한 쪽에서는 생산한 값을 소비하고 있습니다. Unity등 C#에서는 정말로 많이 쓰이는 코루틴 기법입니다.
cotask_enumarator
co_yield를 처리하기 위하여 cotask_enumarator 클래스를 정의하였습니다. 해당 클래스는 co_yield로 생산된 값을 받아서 값을 호출한 쪽에 리턴하는 역활을 합니다. co_return과 같은 경우 void로 처리하고 있습니다. C# 등에서 보통 이런 식으로 처리하기 때문에 co_return에서 값을 받지 않도록 만들었습니다. 만약 co_yield로 값을 생성하는 것 이외에 co_return로 값을 리턴하는 식으로 처리하기를 원한다면 클래스의 void return_void() {} 부분을 이전 예제의 cotask_return클래스의 return처리 부분처럼 수정하여 주시면 됩니다.
co_yield
co_yield는 값을 생산하고 실행을 중간에 멈춥니다. 값을 리턴하고 실행을 멈추고 제어권을 넘기는 역활을 하는데 co_return과 틀린 점은 코루틴 함수 실행을 종료하지는 않습니다.
'강좌' 카테고리의 다른 글
C++ 코루틴(coroutine) 활용 강좌: #3 : co_return (0) | 2022.05.23 |
---|---|
C++ 코루틴(coroutine) 활용 강좌: #2 : co_await (0) | 2022.05.20 |
C++ 코루틴(coroutine) 활용 강좌: #1 시작하며 그리고 기본 개념 (0) | 2022.05.19 |
C++ REST API 사용 강좌 : #2 HTTP POST (boost beast 활용) (2) | 2022.05.12 |
C++ REST API 사용 강좌 : #1 HTTP GET (boost beast 활용) (0) | 2022.05.11 |
C++ boost 메모리풀 강좌 #5: boost 메모리풀 할당자를 적용하여 STL 컬렉션의 성능 향상시키기 (0) | 2021.11.15 |
C++ boost 메모리풀 강좌 #4: object_pool 클래스를 이용하여 new 재정의 없이 일반 클래스에 메모리풀 적용하기 (0) | 2021.11.14 |
C++ boost 메모리풀 강좌 #3: singleton_pool 클래스를 이용하여 일반 클래스에 메모리풀 적용하기 (0) | 2021.11.13 |