如何在 C++ 中为自定义生成器实现重置功能?
How to implement a resetting function for custom generator in c++?
我创建了一个名为 zgenerator
的 class:
template <typename T>
class zgenerator {
public:
class promise_type;
using handle = std::coroutine_handle<promise_type>;
private:
handle coro;
explicit zgenerator(handle h) : coro{h} {}
public:
// default constructor
zgenerator() noexcept : coro{nullptr} {}
// move constructor and assignment
zgenerator(zgenerator&& other) noexcept : coro{std::move(other.coro)} { }
zgenerator& operator=(zgenerator&& other) noexcept {
coro = std::move(other.coro);
return *this;
}
// deleting copy constructor and assignment
zgenerator(const zgenerator&) = delete;
zgenerator& operator=(const zgenerator&) = delete;
// destructor
~zgenerator() { if (coro) coro.destroy(); }
// promise type
class promise_type {
public:
using value_type = std::remove_reference_t<T>;
using reference_type = std::conditional_t<std::is_reference_v<T>, T, T&>;
using pointer_type = value_type*;
private:
pointer_type m_value;
std::exception_ptr m_except;
public:
zgenerator get_return_object() noexcept {
return zgenerator{handle::from_promise(*this)};
}
static auto get_return_object_on_allocation_failure() {
return zgenerator{nullptr};
}
std::suspend_always initial_suspend() const noexcept { return {}; }
std::suspend_always final_suspend() const noexcept { return {}; }
// no 'co_await'
template <typename T1> std::suspend_never await_transform(T1&&) = delete;
std::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
requires std::is_rvalue_reference_v<T>
{
m_value = std::addressof(value);
return {};
}
std::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept {
m_value = std::addressof(value);
return {};
}
void unhandled_exception() {
m_except = std::current_exception();
}
void rethrow_if_exception() {
if (m_except)
std::rethrow_exception(m_except);
}
void return_void() {}
reference_type value() const noexcept {
return static_cast<reference_type>(*m_value);
}
};
// sentinel
struct sentinel {};
// iterator
class iterator {
private:
handle m_coro;
public:
using value_type = typename promise_type::value_type;
using reference = typename promise_type::reference_type;
using pointer = typename promise_type::pointer_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
iterator() noexcept : m_coro{nullptr} {}
explicit iterator(handle other) noexcept : m_coro{other} {}
friend bool operator==(const iterator& it, sentinel) noexcept {
return !it.m_coro || it.m_coro.done();
}
friend bool operator==(sentinel s, const iterator& it) noexcept {
return (it == s);
}
iterator& operator++() {
m_coro.resume();
if (m_coro.done()) {
m_coro.promise().rethrow_if_exception();
}
return *this;
}
void operator++(int) {
(void) this->operator++();
}
reference operator*() const noexcept {
return m_coro.promise().value();
}
pointer operator->() const noexcept {
return std::addressof(this->operator*());
}
};
// iterators
iterator begin() {
if (coro) {
coro.resume();
if (coro.done()) {
coro.promise().rethrow_if_exception();
}
}
return iterator{coro};
}
sentinel end() noexcept {
return sentinel{};
}
void swap(zgenerator& other) noexcept {
using std::swap;
swap(coro, other.coro);
}
// manual yielding
T value() { return coro.promise().value(); }
bool next() {
if (coro) {
coro.resume();
return !coro.done();
} else
return false;
}
bool is_done() { return coro.done(); }
};
这是应用程序:
auto generate_num = [] -> zgenerator<int> {
co_yield 1;
co_yield 3;
co_yield 10;
co_yield -1;
};
auto it = generate_num();
// Used in for-loop:
for (const auto& i : it)
std::cout << i << ' ';
std::cout << '\n';
输出:
1 3 10 -1
我想将对象重置回第一个状态。当我试图从 it
重新分配一个新的生成器对象时,它抛出一个运行时错误:
it = generate_num();
期望:
// reset:
it.reset();
it.next();
while (!it.is_done()) {
std::cout << it.value() << ' ';
it.next();
}
// now printing twice if we include this.
如何在我的发电机中实现重置功能?
您的移动构造函数和赋值需要确保 other.coro
不会仍然指向它以前的位置,否则您会双重破坏它。
zgenerator(zgenerator&& other) noexcept : coro{std::exchange(other.coro, {})} { }
zgenerator& operator=(zgenerator&& other) noexcept {
std::swap(coro, other.coro);
return *this;
}
我创建了一个名为 zgenerator
的 class:
template <typename T>
class zgenerator {
public:
class promise_type;
using handle = std::coroutine_handle<promise_type>;
private:
handle coro;
explicit zgenerator(handle h) : coro{h} {}
public:
// default constructor
zgenerator() noexcept : coro{nullptr} {}
// move constructor and assignment
zgenerator(zgenerator&& other) noexcept : coro{std::move(other.coro)} { }
zgenerator& operator=(zgenerator&& other) noexcept {
coro = std::move(other.coro);
return *this;
}
// deleting copy constructor and assignment
zgenerator(const zgenerator&) = delete;
zgenerator& operator=(const zgenerator&) = delete;
// destructor
~zgenerator() { if (coro) coro.destroy(); }
// promise type
class promise_type {
public:
using value_type = std::remove_reference_t<T>;
using reference_type = std::conditional_t<std::is_reference_v<T>, T, T&>;
using pointer_type = value_type*;
private:
pointer_type m_value;
std::exception_ptr m_except;
public:
zgenerator get_return_object() noexcept {
return zgenerator{handle::from_promise(*this)};
}
static auto get_return_object_on_allocation_failure() {
return zgenerator{nullptr};
}
std::suspend_always initial_suspend() const noexcept { return {}; }
std::suspend_always final_suspend() const noexcept { return {}; }
// no 'co_await'
template <typename T1> std::suspend_never await_transform(T1&&) = delete;
std::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
requires std::is_rvalue_reference_v<T>
{
m_value = std::addressof(value);
return {};
}
std::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept {
m_value = std::addressof(value);
return {};
}
void unhandled_exception() {
m_except = std::current_exception();
}
void rethrow_if_exception() {
if (m_except)
std::rethrow_exception(m_except);
}
void return_void() {}
reference_type value() const noexcept {
return static_cast<reference_type>(*m_value);
}
};
// sentinel
struct sentinel {};
// iterator
class iterator {
private:
handle m_coro;
public:
using value_type = typename promise_type::value_type;
using reference = typename promise_type::reference_type;
using pointer = typename promise_type::pointer_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
iterator() noexcept : m_coro{nullptr} {}
explicit iterator(handle other) noexcept : m_coro{other} {}
friend bool operator==(const iterator& it, sentinel) noexcept {
return !it.m_coro || it.m_coro.done();
}
friend bool operator==(sentinel s, const iterator& it) noexcept {
return (it == s);
}
iterator& operator++() {
m_coro.resume();
if (m_coro.done()) {
m_coro.promise().rethrow_if_exception();
}
return *this;
}
void operator++(int) {
(void) this->operator++();
}
reference operator*() const noexcept {
return m_coro.promise().value();
}
pointer operator->() const noexcept {
return std::addressof(this->operator*());
}
};
// iterators
iterator begin() {
if (coro) {
coro.resume();
if (coro.done()) {
coro.promise().rethrow_if_exception();
}
}
return iterator{coro};
}
sentinel end() noexcept {
return sentinel{};
}
void swap(zgenerator& other) noexcept {
using std::swap;
swap(coro, other.coro);
}
// manual yielding
T value() { return coro.promise().value(); }
bool next() {
if (coro) {
coro.resume();
return !coro.done();
} else
return false;
}
bool is_done() { return coro.done(); }
};
这是应用程序:
auto generate_num = [] -> zgenerator<int> {
co_yield 1;
co_yield 3;
co_yield 10;
co_yield -1;
};
auto it = generate_num();
// Used in for-loop:
for (const auto& i : it)
std::cout << i << ' ';
std::cout << '\n';
输出:
1 3 10 -1
我想将对象重置回第一个状态。当我试图从 it
重新分配一个新的生成器对象时,它抛出一个运行时错误:
it = generate_num();
期望:
// reset:
it.reset();
it.next();
while (!it.is_done()) {
std::cout << it.value() << ' ';
it.next();
}
// now printing twice if we include this.
如何在我的发电机中实现重置功能?
您的移动构造函数和赋值需要确保 other.coro
不会仍然指向它以前的位置,否则您会双重破坏它。
zgenerator(zgenerator&& other) noexcept : coro{std::exchange(other.coro, {})} { }
zgenerator& operator=(zgenerator&& other) noexcept {
std::swap(coro, other.coro);
return *this;
}