I'm making an attempt to learn and implement C++20 coroutines, and I'm experiencing a bug.
Generator class:
template<class ReturnType = void>
class enumerable
{
public:
class promise_type;
using handle_type = std::coroutine_handle<promise_type>;
class promise_type
{
public:
ReturnType current_value{};
auto get_return_object()
{
return enumerable{ handle_type::from_promise(*this) };
}
auto initial_suspend()
{
return std::suspend_always{};
}
auto final_suspend() noexcept
{
return std::suspend_always();
}
void unhandled_exception()
{
// TODO:
}
void return_void()
{
}
auto yield_value(ReturnType& value) noexcept
{
current_value = std::move(value);
return std::suspend_always{};
}
auto yield_value(ReturnType&& value) noexcept
{
return yield_value(value);
}
};
class iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = ReturnType;
using pointer = ReturnType*;
using reference = ReturnType&;
private:
handle_type handle;
public:
iterator(handle_type handle)
: handle(handle)
{
}
reference operator*() const
{
return this->handle.promise().current_value;
}
pointer operator->()
{
return &this->handle.promise().current_value;
}
iterator& operator++()
{
this->handle.resume();
return *this;
}
friend bool operator==(const iterator& it, std::default_sentinel_t s) noexcept
{
return !it.handle || it.handle.done();
}
friend bool operator!=(const iterator& it, std::default_sentinel_t s) noexcept
{
return !(it == s);
}
friend bool operator==(std::default_sentinel_t s, const iterator& it) noexcept
{
return (it == s);
}
friend bool operator!=(std::default_sentinel_t s, const iterator& it) noexcept
{
return it != s;
}
};
handle_type handle;
enumerable() = delete;
enumerable(handle_type h)
: handle(h)
{
std::cout << "enumerable constructed: " << this << " : " << this->handle.address() << '\n';
};
iterator begin()
{
this->handle.resume();
return iterator(this->handle);
}
std::default_sentinel_t end()
{
return {};
}
//Filters a sequence of values based on a predicate.
template<class Predicate>
enumerable<ReturnType> where(Predicate&& pred)
{
std::cout << "where: " << this << " : " << this->handle.address() << '\n';
for (auto& i : *this)
{
if(pred(i))
co_yield i;
}
}
~enumerable()
{
std::cout << "enumerable destructed: " << this << " : " << this->handle.address() << '\n';
}
};
Test code:
enumerable<int> numbers()
{
co_yield 1;
co_yield 2;
co_yield 3;
co_yield 4;
}
enumerable<int> filtered_numbers()
{
return numbers().where([](int i) { return true; });
}
// Crashes
int main()
{
for (auto& i : filtered_numbers())
{
std::cout << "value: " << i << '\n';
}
return 0;
}
Output:
enumerable constructed: 000000FF0550F560:000002959E3B5290
enumerable constructed: 000000FF0550F5D8:000002959E3B6470
destructed: 000000FF0550F560 : 000002959E3B5290
where: 000000FF0550F560 : 000000FF0550F640
//Works, despite "this" inside "where" still being destructed before use, can be observed with the couts.
int main()
{
for(auto i : numbers().where([](int i) { return true; }))
{
std::cout << "value: " << i << '\n';
}
return 0;
}
Output:
enumerable constructed: 000000C9EDD2FD78:000001DADD1A61D0
enumerable constructed: 000000C9EDD2FD28:000001DADD1A73B0
destructed: 000000C9EDD2FD78 : 000001DADD1A61D0
where: 000000C9EDD2FD78 : 000001DADD1A61D0
value: 1
value: 2
value: 3
value: 4
destructed: 000000C9EDD2FD28 : 000001DADD1A73B0
Could somebody explain what is happening here? I'd like to come up with a workaround if possible, the crash does not happen if we return "std::suspend_never" in our promise_type's "initial_suspend", but suspending in initial_suspend isn't ideal behavior for a generator.
This:
return numbers().where([](int i) { return true; });
Creates a temporary (numbers()), then stores a reference to that temporary in a coroutine (the *this used in the loop), and then the temporary goes away.
That's bad. If you want to do chaining of coroutines, each step in that chain needs to be an object on someone's stack. where could be a non-member function that takes an enumerable by value. That would allow the where coroutine to preserve the existence of the enumerable.
//Filters a sequence of values based on a predicate.
template<class Predicate>
enumerable<ReturnType> where(Predicate pred)&
{
std::cout << "where: " << this << " : " << this->handle.address() << '\n';
for (auto& i : *this)
{
if(pred(i))
co_yield i;
}
}
//Captures *this as well as above.
template<class Predicate>
enumerable<ReturnType> where(Predicate pred)&&
{
auto self=std::move(*this);
std::cout << "where: " << this << " : " << this->handle.address() << '\n';
for (auto& i : self)
{
if(pred(i))
co_yield i;
}
}
two changes.
I take Predicate by value, to avoid dangling reference problem.
I have a && overload that copies *this (well, moves from) and stores it within the coroutine.
This still doesn't work.
The first thing that happens is that our coroutine is suspended before any code is run. So the copy of auto self=std::move(*this) happens on the first time we try to get a value.
We can work around this in a few ways. One of them is to bounce to a free function and let it copy the enumerable<int>:
template<class Predicate>
friend enumerable<ReturnType> where( enumerable<ReturnType> self, Predicate pred ) {
for (auto& i: self)
if (pred(i))
co_yield i;
}
//Filters a sequence of values based on a predicate.
template<class Predicate>
enumerable<ReturnType> where(Predicate pred)&
{
return where( *this, std::move(pred) );
}
template<class Predicate>
enumerable<ReturnType> where(Predicate pred)&&
{
return where( std::move(*this), std::move(pred) );
}
a second way is to modify enumerable<ReturnType> to support a setup phase.
struct init_done {};
auto initial_suspend() {
return std::suspend_never{};
}
auto yield_value(init_done) noexcept {
return std::suspend_always{};
}
and modify enumerable<int> returning functions to first co_yield init_done{}; after their setup is finished.
We'd do this on the first line of the numbers() coroutine, and after we copy *this into the local variable self in the where() coroutine.
This is probably simplest:
template<class F>
friend
enumerable<ReturnType> where2(enumerable<ReturnType> self, F f )
{
for (auto i : self.where(std::move(f)))
co_yield i;
}
template<class F>
enumerable<ReturnType> where(F f)&&
{
return where2(std::move(*this), std::move(f));
}
template<class F>
enumerable<ReturnType> where(F f)&
{
for (auto i : *this)
{
if (f(i))
co_yield i;
}
}
Related
I watched the Björn Fahller - Asynchronous I/O and coroutines for smooth data streaming - Meeting C++ online talk. Following up this presentation, I gave a try to execute a similar example myself. There is a bug in my code and when yield is called , the value that is printed is zero. Debugging the code , I detected that the yield_value comparing with await_resume, is called from different promise object.
I am confused, and I do not know how to call the yield_value using the correct promise object.
#include <iostream>
#include <coroutine>
#include <optional>
#include <string>
#include <memory>
using namespace std;
template<typename T>
struct promise;
template<typename T>
struct task
{
using promise_type = promise<T>;
auto operator co_await() const noexcept
{
struct awaitable
{
awaitable(promise<T> & promise)
:m_promise(promise)
{
}
bool await_ready() const noexcept
{
return m_promise.isready();
}
void await_suspend(coroutine_handle<promise_type> next)
{
m_promise.m_continuation = next;
}
T await_resume() const
{
std::cout << "await_resume m_promise::" << &m_promise << std::endl;
return m_promise.get();
}
promise<T> & m_promise;
};
return awaitable(_coroutine.promise());
}
task(promise_type& promise) : _coroutine(coroutine_handle<promise_type>::from_promise(promise))
{
promise.m_continuation = _coroutine;
}
task() = default;
task(task const&) = delete;
task& operator=(task const&) = delete;
task(task && other) : _coroutine(other._coroutine)
{
other._coroutine = nullptr;
}
task& operator=(task&& other)
{
if (&other != this) {
_coroutine = other._coroutine;
other._coroutine = nullptr;
}
return *this;
}
static task<T> make()
{
std::cout << "Enter make" << std::endl;
co_await suspend_always{};
std::cout << "Enter exit" << std::endl;
}
auto get_promise()
{
std::cout << "get_promise " << &_coroutine.promise() << std::endl;
return _coroutine.promise();
}
~task()
{
if (_coroutine) {
_coroutine.destroy();
}
}
private:
friend class promise<T>;
coroutine_handle<promise_type> _coroutine;
};
template<typename T>
struct promise
{
task<T> get_return_object() noexcept
{
return {*this};
}
suspend_never initial_suspend() noexcept{return {};}
suspend_always final_suspend() noexcept{return {};}
bool isready() const noexcept
{
return m_value.has_value();
}
T get()
{
return m_value.has_value()? m_value.value(): 0;
}
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;
}
template<typename U>
suspend_always yield_value(U && u)
{
std::cout << "yield_value::" << &m_continuation.promise() << std::endl;
m_value.emplace(std::forward<U>(u));
m_continuation.resume();
//m_continuation.
return {};
}
void return_void(){}
coroutine_handle<promise<T>> m_continuation;
optional<T> m_value;
};
template<typename T>
task<T> print_all(task<T> & values)
{
std::cout << "print all" << std::endl;
for(;;)
{
auto v = co_await values;
std::cout << v << "\n" << std::flush;
}
}
int main(int argc, const char * argv[]) {
auto incoming = task<int>::make();
auto h = print_all(incoming);
auto promise = incoming.get_promise();
promise.yield_value(4);
}
Any help?
demo
This is returning a copy of the promise:
auto get_promise()
{
std::cout << "get_promise " << &_coroutine.promise() << std::endl;
return _coroutine.promise();
}
So instead of calling into the promise for the task, you're calling into just some other, unrelated promise object.
Once you fix that, you'll find that your code has an infinite loop. Your promise is "ready" when it has a value. But once it has a value, it always has a value - it's always ready. One way to fix this is to ensure that await_resume consumes the value. For instance, by changing get() to:
T get()
{
assert(m_value.has_value());
T v = *std::move(m_value);
m_value.reset();
return v;
}
That ensures that the next co_await actually suspends.
Watching a c++ lecture (https://youtu.be/DLLt4anKXKU?t=1589), I tried to understand how future work with co_await; example:
auto compute = []() -> std::future<int> {
int fst = co_await std::async(get_first);
int snd = co_await std::async(get_second);
co_return fst + snd;
};
auto f = compute();
/* some heavy task */
f.get();
I can't understand how and when co_await std::async(get_first) returns control to compute. i.e how std::future implements an awaitable interface (type).
how std::future implements an awaitable interface
Well as far as C++20 is concerned, it doesn't. C++20 provides co_await and its attendant language functionality, but it doesn't provide any actual awaitable types.
How std::future could implement the awaitable interface is basically the same as how std::experimental::future from the Concurrency TS implements future::then. then takes a function to be continued when the future's value becomes available. The return value of then is a new future<U> (the old future<T> now becomes non-functional), where U is the new value that the given continuation function returns. That new future will only have a U available when the original value is available and when the continuation has processed it into the new value. In that order.
The exact details about how .then works depend entirely on how future is implemented. And it may depend on how the specific future was created, as futures from std::async have special properties that other futures don't.
co_await just makes this process much more digestible visually. A co_awaitable future would simply shove the coroutine handle into future::then, thereby altering the future.
Here there is a full program that can await futures with C++20 coroutines. I did it myself these days to learn.
#include <cassert>
#include <coroutine>
#include <future>
#include <iostream>
#include <optional>
#include <thread>
using namespace std::literals;
template <class T>
class FutureAwaitable {
public:
template <class U> struct BasicPromiseType {
auto get_return_object() {
return FutureAwaitable<T>(CoroHandle::from_promise(*this));
}
std::suspend_always initial_suspend() noexcept {
std::cout << "Initial suspend\n";
return {};
}
std::suspend_never final_suspend() noexcept {
std::cout << "Final suspend\n";
return {};
}
template <class V>
requires std::is_convertible_v<V, T>
void return_value(V v) { _value = v; }
void unhandled_exception() { throw; }
std::optional<T> _value;
};
using promise_type = BasicPromiseType<FutureAwaitable<T>>;
using CoroHandle = std::coroutine_handle<promise_type>;
explicit FutureAwaitable(CoroHandle h) : _parent(h) { }
~FutureAwaitable() {
}
bool is_ready() const {
auto & fut = std::get<FutureAwaitable<T> *>(&_parent);
return fut->wait_for(std::chrono::seconds(0)) != std::future_status::ready;
}
FutureAwaitable(std::future<T> && f) {
_f = &f;
}
T get() const { return promise()._value.value(); }
std::future<T> & std_future() const {
assert(_f->valid());
return *_f;
}
bool await_ready() {
if (!(_f->wait_for(std::chrono::seconds(0)) == std::future_status::ready)) {
std::cout << "Await ready IS ready\n";
return true;
}
else
std::cout << "Await ready NOT ready\n";
return false;
}
auto await_resume() {
std::cout << "Await resume" << std::endl;
return std_future().get();
}
bool await_suspend(CoroHandle parent) {
_parent = parent;
std::cout << "Await suspend\n";
return true;
}
void resume() {
assert(_parent);
_parent.resume();
}
auto parent() const { return _parent; }
bool done() const noexcept {
return _parent.done();
}
private:
auto & promise() const noexcept { return _parent.promise(); }
CoroHandle _parent = nullptr;
std::future<T> * _f = nullptr;
};
template <class T> auto operator co_await(std::future<T> &&f) {
return FutureAwaitable<T>(std::forward<std::future<T>>(f));
}
template <class T> auto operator co_await(std::future<T> & f) {
return FutureAwaitable<T>(std::forward<std::future<T>>(f));
}
FutureAwaitable<int> coroutine() {
std::promise<int> p;
auto fut = p.get_future();
p.set_value(31);
std::cout << "Entered func()" << std::endl;
auto res = co_await std::move(fut);
std::cout << "Continue func(): " << res << std::endl;
auto computation = co_await std::async(std::launch::async, [] {
int j = 0;
for (int i = 0; i < 1000; ++i) {
j += i;
}
return j;
});
auto computation2 = std::async(std::launch::async, [] {
int j = 0;
std::this_thread::sleep_for(20s);
for (int i = 0; i < 1000; ++i) {
j += i;
}
return j;
});
auto computation3 = std::async(std::launch::async, [] {
int j = 0;
std::this_thread::sleep_for(20s);
for (int i = 0; i < 1000; ++i) {
j += i;
}
return j;
});
co_await computation2;
co_await computation3;
std::cout << "Computation result is " << computation << std::endl;
co_return computation;
}
#define ASYNC_MAIN(coro) \
int main() { \
FutureAwaitable<int> c = coro(); \
do { c.resume(); } while (!c.done()); \
std::cout << "The coroutine returned " << c.get(); \
return 0; \
}
ASYNC_MAIN(coroutine)
Just for learning purpose I've tried to make overcomplicated "Hello World" program with C++20 coroutines:
HelloWorldMessage sayHelloToWorld()
{
co_yield "Hello";
co_yield " ";
co_yield "World";
co_yield "!";
}
int main()
{
for (auto w : sayHelloToWorld())
{
std::cout << w;
}
}
To prepare such HelloWorldMessage generator I based mainly on latest clang warning messages and, uncomplete cppreference page and this example.
So my result below. What is missing here? Because, instead of saying Hello, I got segmentation fault:
See link:
struct HelloWorldState
{
const char* currentWord = "<not value yet>";
bool finalWord = false;
};
struct HelloWorldPromise
{
HelloWorldState state;
std::experimental::suspend_always initial_suspend() const noexcept { return {}; }
std::experimental::suspend_always final_suspend() const noexcept { return {}; }
std::experimental::suspend_always yield_value(const char* word) noexcept
{
state.currentWord = word;
return {};
}
std::experimental::suspend_always return_void() noexcept
{
state.finalWord = true;
return {};
}
auto& get_return_object() noexcept
{
return *this;
}
void unhandled_exception()
{
state.finalWord = true;
throw;
}
};
struct HelloWorldMessage
{
using promise_type = HelloWorldPromise;
using promise_handle = std::experimental::coroutine_handle<promise_type>;
struct Iter
{
promise_handle handle = nullptr;
HelloWorldState state;
using iterator_category = std::input_iterator_tag;
using value_type = const char*;
using difference_type = ptrdiff_t;
using pointer = value_type const *;
using reference = value_type const &;
reference operator * () const { assert(handle); return state.currentWord; }
pointer operator -> () const { return std::addressof(operator*()); }
bool operator == (const Iter& other) { return handle == other.handle; }
bool operator != (const Iter& other) { return !(*this == other); }
Iter() = default;
Iter(promise_handle handle)
: handle(handle)
{
assert(handle);
next();
}
Iter& operator ++()
{
if (!handle)
return *this;
if (state.finalWord)
{
handle = nullptr;
return *this;
}
next();
return *this;
}
void next()
{
try {
handle.resume();
state = handle.promise().state;
} catch (...) {
std::cerr << "#%$##%##$% \n";
}
}
};
promise_handle handle = nullptr;
HelloWorldMessage(promise_type& promise) : handle(promise_handle::from_promise(promise)) {}
Iter begin() const { assert(handle); return {handle}; }
Iter end() const { return {}; }
};
Maybe clang is not ready yet?
A few mistakes:
First - promise shall return generator object, not reference to itself. So the proper way is:
struct HelloWorldPromise
{
...
auto get_return_object();
...
};
struct HelloWorldMessage
{
...
};
auto HelloWorldPromise::get_return_object()
{
return HelloWorldMessage(*this);
}
Next - terminate and return void can be simplified to:
void return_void() noexcept
{}
void unhandled_exception()
{
std::terminate();
}
Next - in iterator - we shall rely on handle.done - so the state.finalWord is not needed. Full iterator source is:
struct Iter
{
promise_handle handle = nullptr;
HelloWorldState state;
reference operator * () const { return state.currentWord; }
pointer operator -> () const { return std::addressof(operator*()); }
bool operator == (const Iter& other) const { return !handle == !other.handle; }
bool operator != (const Iter& other) const { return !(*this == other); }
Iter() = default;
Iter(promise_handle handle)
: handle(handle)
{
next();
}
Iter& operator ++()
{
if (!handle)
return *this;
next();
return *this;
}
void next()
{
if (!handle)
return;
try {
handle.resume();
if (!handle.done())
state = handle.promise().state;
else {
handle = nullptr;
}
} catch (...) {
std::cerr << "#%$##%##$% \n";
}
}
};
And full working example here.
I take most of my corrections from this 2018/n4736.pdf.
I implemented my first custom iterator, which is supposed to return an std::pair at each iteration. The only problem, is it loops infinitely and I do not know how to provide stop condition. The code looks like so:
#include <iostream>
#include <vector>
#include <utility>
class Simple
{
private:
std::vector<std::size_t> indices;
std::vector<int> values;
public:
void insert(std::size_t index, int value)
{
indices.push_back(index);
values.push_back(value);
}
int at(std::size_t index)
{
return values[indices[index]];
}
class Iterator
{
private:
const std::vector<std::size_t>* indices;
const std::vector<int>* values;
std::size_t pos = 0;
public:
Iterator(const std::vector<std::size_t>* indices_, const std::vector<int>* values_, const std::size_t &pos_ = 0):
values(values_), indices(indices_), pos(pos_){ }
bool operator==(const Iterator& other){
return this == &other;
}
bool operator!=(const Iterator& other){
return !operator==(other);
}
Iterator operator++() {
pos++;
Iterator i = *this;
return i;
}
std::pair<std::size_t, int> operator*()
{
if (pos < (*values).size())
{
return std::make_pair((*indices)[pos], (*values)[pos]);
}
std::cout << "EMPTY PAIR" << std::endl; //loops infinitely and prints this message
return std::pair<std::size_t,int>{};
}
std::pair<std::size_t, int>* operator->()
{
if (pos < (*values).size())
{
std::pair<std::size_t,int> *p;
*p = std::make_pair((*indices)[pos], (*values)[pos]);
return p;
}
return nullptr;
}
};
Iterator begin() const
{
return Iterator(&indices, &values, 0);
}
Iterator end() const
{
return Iterator(&indices, &values, values.size());
}
};
int main() {
Simple s;
s.insert(10, 100);
std::cout << s.at(10) << std::endl;
int i = 0;
for (const std::pair<std::size_t, int> &p : s)
{
std::cout << p.first << " " << p.second << std::endl;
if (i > 3) break; // otherwise it will loop infinitely
i++;
}
return 0;
}
I would like to stress, that this example is simplified and for demonstration purposes, so you may not ask me, why I'm not using map or unorded_map (just because what I'm implementing looks like a map). The question is solely about iterator and I need understanding what may be wrong with iterator itself and how to provide stop condition.
C++11 hasn't range-based-loop for ranged integral sequence.
for(auto e : {0..10} ) // wouldn't compile!!!
So I just decided simulate it.
template< class T , bool enable = std::is_integral<T>::value >
struct range_impl
{
struct iterator
{
constexpr T operator * ()const noexcept { return value; }
iterator& operator ++()noexcept { ++value; return *this; }
friend
constexpr bool operator != (const iterator & lhs, const iterator rhs ) noexcept
{
return lhs.value != rhs.value;
}
T value;
};
constexpr iterator begin()const noexcept { return { first }; }
constexpr iterator end ()const noexcept { return { last }; }
T first;
T last ;
};
template< class T >
range_impl<T> range(T first , T last) noexcept
{
return {first, last};
}
int main(){
// print numbers in [ 0..10 ), i.e. 0 1 2 3 4 5 6 7 8 9
for(auto e : range(0,10) ) std::cout << e << ' ';
std::cout << std::endl;
}
Q: How to generalize this method for ForwardIterators?
example:
template< class ForwardIterator, class T >
bool find(ForwardIterator first, ForwardIterator last, T const& value)
{
for(auto e: range(first, last) ) if (e == v) return true;
return false;
}
Specialization
template< class Iterator>
struct range_impl<Iterator, false>
{
range_impl(Iterator first, Iterator last)
: first(first), last(last)
{}
constexpr Iterator begin()const noexcept { return { first }; }
constexpr Iterator end ()const noexcept { return { last }; }
Iterator first;
Iterator last ;
};
Test:
int main(){
for(auto e : range(0,10) ) std::cout << e << ' ';
std::cout << std::endl;
const char* a[] = { "Say", "hello", "to", "the", "world" };
for(auto e : range(a, a + 5) ) std::cout << e << ' ';
std::cout << std::endl;
}
You are trying to reimplement boost::iterator_range and boost::counting_iterator. Simply do this instead:
template< class T >
boost::iterator_range< boost::counting_iterator<T> > range( T const& tBegin, T const& tEnd ) {
return boost::iterator_range< boost::counting_iterator<T> >( tBegin, tEnd );
}
There even exists a boost::counting_range already: http://www.boost.org/doc/libs/1_47_0/libs/range/doc/html/range/reference/ranges/counting_range.html