はじめに
自作クラスを範囲forにぶちこみたいときに、begin
end
を定義したりするわけですが
その時に書くイテレータを直感的に書きたいなと思い
C++20コルーチンを使ってみました。
実装
基本的にはgeneratorっぽい実装をイテレータのみに集約した感じです。
※generatorの概念についてはここでは特に触れません。以下などを参考にしてください。
https://qiita.com/yohhoy/items/aeb3c01d02d0f640c067
co_iterator.hpp
# pragma once
# include <coroutine>
struct co_iterator_end {};
template<class Type>
struct co_iterator
{
struct promise_type
{
Type value;
auto get_return_object() { return co_iterator{ handle::from_promise(*this) }; }
auto initial_suspend() { return std::suspend_never{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
auto yield_value(const Type& _value)
{
this->value = _value;
return std::suspend_always{};
}
void unhandled_exception() { std::terminate(); }
void return_void() {}
};
using handle = std::coroutine_handle<promise_type>;
public:
explicit co_iterator(handle h)
: coro(h)
{}
co_iterator(const co_iterator&) = delete;
co_iterator(co_iterator&& rhs) noexcept
: coro(std::move(rhs.coro))
{
rhs.coro = nullptr;
}
~co_iterator()
{
if (coro) {
coro.destroy();
}
}
co_iterator& operator++()
{
if (!coro.done()) {
coro.resume();
}
return *this;
}
decltype(auto) operator*()
{
return coro.promise().value;
}
bool operator!=(const co_iterator_end&) const
{
return !coro.done();
}
private:
handle coro;
};
使い方
main.cpp
struct Vector3
{
int x, y, z;
co_iterator<int> begin() const
{
// コルーチンで書けるとわかりやすい
co_yield x;
co_yield y;
co_yield z;
}
co_iterator_end end() const
{
return {};
}
};
int main()
{
Vector3 vec{1,2,3};
for(auto&& elm : vec) {
std::cout << elm << std::endl;
}
}
output
1
2
3
まとめ
- C++20コルーチン楽しい
- C++20コルーチン便利
- (generatorでも似たようなことができるよ)