使用协程在 C++ 中创建生成器
Create a generator in C++ using coroutines
根据McNellis对协程的介绍(Introduction to C Coroutines),我试着编译了一个简单协程的例子,其实就是一个简单的计数器。
然而,当 resume 被调用时,断言被触发。
具体
_LIBCPP_ASSERT(!done(),
"resume() has undefined behaviour when the coroutine is done");
我对协程的经验不多,我找不到问题所在。
代码如下
#include <iostream>
#include <coroutine>
using namespace std;
//using namespace experimental;
struct resumable_thing
{
struct promise_type
{
int const* _current;
promise_type & get_return_object() noexcept
{
return *this;
}
auto initial_suspend() { return suspend_always{}; }
auto final_suspend() { return suspend_always{}; }
auto yield_value(int const& value) {
_current = &value;
return suspend_always{};
}
void unhandled_exception()
{
auto ex = std::current_exception();
std::rethrow_exception(ex);
//// MSVC bug? should be possible to rethrow with "throw;"
//// rethrow exception immediately
// throw;
}
void return_void() noexcept
{
}
};
// using coroutine_handle = coroutine_handle<promise_type>;
resumable_thing() = default;
resumable_thing(resumable_thing const & ) = delete;
resumable_thing & operator = (resumable_thing const & ) =delete;
resumable_thing(promise_type && promise)
: _coroutine(coroutine_handle<promise_type>::from_promise(promise)) {}
resumable_thing(resumable_thing&& other) : _coroutine(other._coroutine)
{
other._coroutine = nullptr;
}
resumable_thing & operator = (resumable_thing const && other)
{
_coroutine = other._coroutine;
_coroutine = nullptr;
return *this;
}
explicit resumable_thing(coroutine_handle<promise_type> coroutine)
:_coroutine(coroutine)
{
}
~resumable_thing()
{
if(_coroutine)
{
_coroutine.destroy();
}
}
void resume()
{
std::cout << "coroutines resume" << std::endl;
_coroutine.resume();
}
coroutine_handle<promise_type> _coroutine = nullptr;
};
resumable_thing counter()
{
cout << "counter: called\n";
for(int i = 0 ; ++i;)
{
co_await suspend_always{};
cout << "counter: resumed (#" << i << ")\n";
}
}
int main(int argc, const char * argv[]) {
// insert code here...
cout << "main: calling counter\n";
resumable_thing the_counter = counter();
cout << "main: resuming counter\n";
the_counter.resume();//assertion is fired
cout << "main:done" << std::endl;
return 0;
}
请查找代码here
promise_type::get_return_object()
应 return 用户定义协程结果类型 resumable_thing
,而不是 promise_type&
本身。
struct resumable_thing
{
struct promise_type
{
resumable_thing get_return_object() noexcept
{
return {*this};
}
// ...
};
resumable_thing(promise_type & promise)
: _coroutine(coroutine_handle<promise_type>::from_promise(promise)) {}
// ...
};
并且 resumable_thing
的移动赋值运算符无意中重置了 this->_coroutine
。
// FIXED: resumable_thing const && -> resumable_thing &&
resumable_thing & operator = (resumable_thing && other)
{
_coroutine = other._coroutine;
// FIXED: _coroutine -> other._coroutine
other._coroutine = nullptr;
return *this;
}
根据McNellis对协程的介绍(Introduction to C Coroutines),我试着编译了一个简单协程的例子,其实就是一个简单的计数器。 然而,当 resume 被调用时,断言被触发。 具体
_LIBCPP_ASSERT(!done(), "resume() has undefined behaviour when the coroutine is done");
我对协程的经验不多,我找不到问题所在。
代码如下
#include <iostream>
#include <coroutine>
using namespace std;
//using namespace experimental;
struct resumable_thing
{
struct promise_type
{
int const* _current;
promise_type & get_return_object() noexcept
{
return *this;
}
auto initial_suspend() { return suspend_always{}; }
auto final_suspend() { return suspend_always{}; }
auto yield_value(int const& value) {
_current = &value;
return suspend_always{};
}
void unhandled_exception()
{
auto ex = std::current_exception();
std::rethrow_exception(ex);
//// MSVC bug? should be possible to rethrow with "throw;"
//// rethrow exception immediately
// throw;
}
void return_void() noexcept
{
}
};
// using coroutine_handle = coroutine_handle<promise_type>;
resumable_thing() = default;
resumable_thing(resumable_thing const & ) = delete;
resumable_thing & operator = (resumable_thing const & ) =delete;
resumable_thing(promise_type && promise)
: _coroutine(coroutine_handle<promise_type>::from_promise(promise)) {}
resumable_thing(resumable_thing&& other) : _coroutine(other._coroutine)
{
other._coroutine = nullptr;
}
resumable_thing & operator = (resumable_thing const && other)
{
_coroutine = other._coroutine;
_coroutine = nullptr;
return *this;
}
explicit resumable_thing(coroutine_handle<promise_type> coroutine)
:_coroutine(coroutine)
{
}
~resumable_thing()
{
if(_coroutine)
{
_coroutine.destroy();
}
}
void resume()
{
std::cout << "coroutines resume" << std::endl;
_coroutine.resume();
}
coroutine_handle<promise_type> _coroutine = nullptr;
};
resumable_thing counter()
{
cout << "counter: called\n";
for(int i = 0 ; ++i;)
{
co_await suspend_always{};
cout << "counter: resumed (#" << i << ")\n";
}
}
int main(int argc, const char * argv[]) {
// insert code here...
cout << "main: calling counter\n";
resumable_thing the_counter = counter();
cout << "main: resuming counter\n";
the_counter.resume();//assertion is fired
cout << "main:done" << std::endl;
return 0;
}
请查找代码here
promise_type::get_return_object()
应 return 用户定义协程结果类型 resumable_thing
,而不是 promise_type&
本身。
struct resumable_thing
{
struct promise_type
{
resumable_thing get_return_object() noexcept
{
return {*this};
}
// ...
};
resumable_thing(promise_type & promise)
: _coroutine(coroutine_handle<promise_type>::from_promise(promise)) {}
// ...
};
并且 resumable_thing
的移动赋值运算符无意中重置了 this->_coroutine
。
// FIXED: resumable_thing const && -> resumable_thing &&
resumable_thing & operator = (resumable_thing && other)
{
_coroutine = other._coroutine;
// FIXED: _coroutine -> other._coroutine
other._coroutine = nullptr;
return *this;
}