Skip to content

Instantly share code, notes, and snippets.

@KayEss
Last active June 17, 2017 04:18
Show Gist options
  • Save KayEss/999f8e3128dd1cd3255b688da8f04f9f to your computer and use it in GitHub Desktop.
Save KayEss/999f8e3128dd1cd3255b688da8f04f9f to your computer and use it in GitHub Desktop.
Coroutine generator
/*
* A generator. Initial code taken from N4649 p15
* http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4649.pdf
*/
#include <experimental/coroutine>
#include <iostream>
struct generator {
struct promise_type;
using handle = std::experimental::coroutine_handle<promise_type>;
struct promise_type {
int current_value;
static auto get_return_object_on_allocation_failure() {
return generator{nullptr};
}
auto get_return_object() {
return generator{handle::from_promise(*this)};
}
auto initial_suspend() {
return std::experimental::suspend_always{};
}
auto final_suspend() {
return std::experimental::suspend_always{};
}
auto yield_value(int value) {
std::cout << "Yielded " << value << std::endl;
current_value = value;
return std::experimental::suspend_always{};
}
auto return_void() {
return std::experimental::suspend_never{};
}
void unhandled_exception() {
std::exit(1);
}
};
bool move_next() {
if ( coro ) {
std::cout << "Moving to next" << std::endl;;
coro.resume();
std::cout << "Are we done? ";
auto still_going = not coro.done();
std::cout << (still_going ? "There is another" : "We're done") << std::endl;
return still_going;
} else {
return false;
}
}
int current_value() {
return coro.promise().current_value;
}
generator(const generator &) = delete;
generator(generator &&g)
: coro(g.coro) {
g.coro = nullptr;
};
~generator() {
if ( coro ) coro.destroy();
}
private:
generator(handle h)
: coro(h) {
}
handle coro;
};
generator f() {
std::cout << "Going to yield 1" << std::endl;
co_yield 1;
std::cout << "Going to yield 2" << std::endl;
co_yield 2;
std::cout << "Going to yield 3" << std::endl;
co_yield 3;
std::cout << "f() is done" << std::endl;
}
int main() {
std::cout << "coro3" << std::endl;
auto g = f();
while ( g.move_next() )
std::cout << ">> " << g.current_value() << std::endl;
return 0;
}
@KayEss
Copy link
Author

KayEss commented Jun 16, 2017

The output from this is (for release builds):

coro3
Moving to next
Going to yield 1
Yielded 1
Are we done? There is another
>> 1
Moving to next

So it doesn't actually work.

Debug works, but gets a double free/memory corruption error on completion.

@KayEss
Copy link
Author

KayEss commented Jun 17, 2017

There is a subtle bug in the code, to do with the way the generator is destructed. Because the destructor will destroy the coroutine we have to be careful about copying the generator instance itself. Making it movable and not copyable fixes this:

generator(const generator &) = delete;
generator(generator &&g)
: coro(g.coro) {
    g.coro = nullptr;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment