forked from OSchip/llvm-project
[coroutines] Add end-to-end tests within libc++
This patch adds end-to-end/breathing tests for coroutines into libc++. The tests aren't specifically to test libc++ requirements but instead are intented to ensure coroutines are working fine in general. Although libc++ isn't exactly the most correct place for these tests to live, there is one major advantage. The libc++ test suite is also used by MSVC and by adding the tests here it ensures they will be run against all currently available coroutine implementations. llvm-svn: 304101
This commit is contained in:
parent
cf09175de8
commit
c34a497b40
|
@ -0,0 +1,72 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
// REQUIRES: fcoroutines-ts
|
||||
|
||||
// RUN: %build -fcoroutines-ts
|
||||
// RUN: %run
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <cassert>
|
||||
|
||||
using namespace std::experimental;
|
||||
|
||||
struct coro_t {
|
||||
struct promise_type {
|
||||
coro_t get_return_object() {
|
||||
coroutine_handle<promise_type>{};
|
||||
return {};
|
||||
}
|
||||
suspend_never initial_suspend() { return {}; }
|
||||
suspend_never final_suspend() { return {}; }
|
||||
void return_void(){}
|
||||
static void unhandled_exception() {}
|
||||
};
|
||||
};
|
||||
|
||||
struct B {
|
||||
~B() {}
|
||||
bool await_ready() { return true; }
|
||||
B await_resume() { return {}; }
|
||||
template <typename F> void await_suspend(F) {}
|
||||
};
|
||||
|
||||
|
||||
struct A {
|
||||
~A(){}
|
||||
bool await_ready() { return true; }
|
||||
int await_resume() { return 42; }
|
||||
template <typename F> void await_suspend(F) {}
|
||||
};
|
||||
|
||||
int last_value = -1;
|
||||
void set_value(int x) {
|
||||
last_value = x;
|
||||
}
|
||||
|
||||
coro_t f(int n) {
|
||||
if (n == 0) {
|
||||
set_value(0);
|
||||
co_return;
|
||||
}
|
||||
int val = co_await A{};
|
||||
set_value(42);
|
||||
}
|
||||
|
||||
coro_t g() { B val = co_await B{}; }
|
||||
|
||||
int main() {
|
||||
last_value = -1;
|
||||
f(0);
|
||||
assert(last_value == 0);
|
||||
f(1);
|
||||
assert(last_value == 42);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
// REQUIRES: fcoroutines-ts
|
||||
|
||||
// RUN: %build -fcoroutines-ts
|
||||
// RUN: %run
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <cassert>
|
||||
|
||||
using namespace std::experimental;
|
||||
|
||||
struct coro_t {
|
||||
struct promise_type {
|
||||
coro_t get_return_object() {
|
||||
coroutine_handle<promise_type>{};
|
||||
return {};
|
||||
}
|
||||
suspend_never initial_suspend() { return {}; }
|
||||
suspend_never final_suspend() { return {}; }
|
||||
void return_void(){}
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
};
|
||||
|
||||
struct NoSuspend {
|
||||
bool await_ready() { return false; }
|
||||
void await_resume() {}
|
||||
template <typename F> bool await_suspend(F) { return false; }
|
||||
};
|
||||
|
||||
struct DoSuspend {
|
||||
bool await_ready() { return false; }
|
||||
void await_resume() {}
|
||||
template <typename F> bool await_suspend(F) { return true; }
|
||||
};
|
||||
|
||||
bool f_started, f_resumed = false;
|
||||
coro_t f() {
|
||||
f_started = true;
|
||||
co_await DoSuspend{};
|
||||
f_resumed = true;
|
||||
}
|
||||
|
||||
bool g_started, g_resumed = false;
|
||||
coro_t g() {
|
||||
g_started = true;
|
||||
co_await NoSuspend{};
|
||||
g_resumed = true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
assert(!f_started && !f_resumed && !g_started && !g_resumed);
|
||||
f();
|
||||
assert(f_started && !f_resumed);
|
||||
g();
|
||||
assert(g_started && g_resumed);
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
// REQUIRES: fcoroutines-ts
|
||||
|
||||
// RUN: %build -fcoroutines-ts
|
||||
// RUN: %run
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <cassert>
|
||||
using namespace std::experimental;
|
||||
|
||||
struct error {};
|
||||
|
||||
template <typename T, typename Error = int>
|
||||
struct expected {
|
||||
|
||||
struct Data {
|
||||
T val;
|
||||
Error error;
|
||||
};
|
||||
Data data;
|
||||
|
||||
struct DataPtr {
|
||||
Data *p;
|
||||
~DataPtr() { delete p; }
|
||||
};
|
||||
|
||||
expected() {}
|
||||
expected(T val) : data{std::move(val),{}} {}
|
||||
expected(struct error, Error error) : data{{}, std::move(error)} {}
|
||||
expected(DataPtr & p) : data{std::move(p.p->val), std::move(p.p->error)} {}
|
||||
|
||||
struct promise_type {
|
||||
Data* data;
|
||||
DataPtr get_return_object() { data = new Data; return {data}; }
|
||||
suspend_never initial_suspend() { return {}; }
|
||||
suspend_never final_suspend() { return {}; }
|
||||
void return_value(T v) { data->val = std::move(v); data->error = {};}
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
|
||||
bool await_ready() { return !data.error; }
|
||||
T await_resume() { return std::move(data.val); }
|
||||
void await_suspend(coroutine_handle<promise_type> h) {
|
||||
h.promise().data->error =std::move(data.error);
|
||||
h.destroy();
|
||||
}
|
||||
|
||||
T const& value() { return data.val; }
|
||||
Error const& error() { return data.error; }
|
||||
};
|
||||
|
||||
expected<int> g() { return {0}; }
|
||||
expected<int> h() { return {error{}, 42}; }
|
||||
|
||||
extern "C" void print(int);
|
||||
|
||||
bool f1_started, f1_resumed = false;
|
||||
expected<int> f1() {
|
||||
f1_started = true;
|
||||
(void)(co_await g());
|
||||
f1_resumed = true;
|
||||
co_return 100;
|
||||
}
|
||||
|
||||
bool f2_started, f2_resumed = false;
|
||||
expected<int> f2() {
|
||||
f2_started = true;
|
||||
(void)(co_await h());
|
||||
f2_resumed = true;
|
||||
co_return 200;
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto c1 = f1();
|
||||
assert(f1_started && f1_resumed);
|
||||
assert(c1.value() == 100);
|
||||
assert(c1.error() == 0);
|
||||
|
||||
auto c2 = f2();
|
||||
assert(f2_started && !f2_resumed);
|
||||
assert(c2.value() == 0);
|
||||
assert(c2.error() == 42);
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
// REQUIRES: fcoroutines-ts
|
||||
|
||||
// RUN: %build -fcoroutines-ts
|
||||
// RUN: %run
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <cassert>
|
||||
|
||||
using namespace std::experimental;
|
||||
|
||||
int alive = 0;
|
||||
int ctor_called = 0;
|
||||
int dtor_called = 0;
|
||||
void reset() {
|
||||
assert(alive == 0);
|
||||
alive = 0;
|
||||
ctor_called = 0;
|
||||
dtor_called = 0;
|
||||
}
|
||||
struct Noisy {
|
||||
Noisy() { ++alive; ++ctor_called; }
|
||||
Noisy(Noisy const&) = delete;
|
||||
~Noisy() { --alive; ++dtor_called; }
|
||||
};
|
||||
|
||||
struct Bug {
|
||||
bool await_ready() { return true; }
|
||||
void await_suspend(std::experimental::coroutine_handle<>) {}
|
||||
Noisy await_resume() { return {}; }
|
||||
};
|
||||
struct coro2 {
|
||||
struct promise_type {
|
||||
suspend_never initial_suspend() { return{}; }
|
||||
suspend_never final_suspend() { return{}; }
|
||||
coro2 get_return_object() { return{}; }
|
||||
void return_void() {}
|
||||
Bug yield_value(int) { return {}; }
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
};
|
||||
|
||||
// Checks that destructors are correctly invoked for the object returned by
|
||||
// coawait.
|
||||
// CHECK-LABEL: @a(
|
||||
coro2 a() {
|
||||
reset();
|
||||
{
|
||||
auto x = co_await Bug{};
|
||||
assert(alive == 1);
|
||||
assert(ctor_called == 1);
|
||||
assert(dtor_called == 0);
|
||||
}
|
||||
assert(alive == 0);
|
||||
assert(dtor_called == 1);
|
||||
}
|
||||
|
||||
coro2 b() {
|
||||
reset();
|
||||
{
|
||||
co_await Bug{};
|
||||
assert(ctor_called == 1);
|
||||
assert(dtor_called == 1);
|
||||
assert(alive == 0);
|
||||
}
|
||||
assert(ctor_called == 1);
|
||||
assert(dtor_called == 1);
|
||||
assert(alive == 0);
|
||||
|
||||
}
|
||||
|
||||
coro2 c() {
|
||||
reset();
|
||||
{
|
||||
auto x = co_yield 42;
|
||||
assert(alive == 1);
|
||||
assert(ctor_called == 1);
|
||||
assert(dtor_called == 0);
|
||||
}
|
||||
assert(alive == 0);
|
||||
assert(ctor_called == 1);
|
||||
assert(dtor_called == 1);
|
||||
}
|
||||
|
||||
coro2 d() {
|
||||
reset();
|
||||
{
|
||||
co_yield 42;
|
||||
assert(ctor_called == 1);
|
||||
assert(dtor_called == 1);
|
||||
assert(alive == 0);
|
||||
}
|
||||
assert(alive == 0);
|
||||
assert(ctor_called == 1);
|
||||
assert(dtor_called == 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
a();
|
||||
b();
|
||||
c();
|
||||
d();
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
// REQUIRES: fcoroutines-ts
|
||||
|
||||
// RUN: %build -fcoroutines-ts
|
||||
// RUN: %run
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
#include "coroutine_types.h"
|
||||
|
||||
using namespace std::experimental;
|
||||
|
||||
struct minig {
|
||||
struct promise_type {
|
||||
int current_value;
|
||||
suspend_always yield_value(int value) {
|
||||
this->current_value = value;
|
||||
return {};
|
||||
}
|
||||
suspend_always initial_suspend() { return {}; }
|
||||
suspend_always final_suspend() { return {}; }
|
||||
minig get_return_object() { return minig{this}; };
|
||||
void return_void() {}
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
|
||||
bool move_next() {
|
||||
p.resume();
|
||||
return !p.done();
|
||||
}
|
||||
int current_value() { return p.promise().current_value; }
|
||||
|
||||
minig(minig &&rhs) : p(rhs.p) { rhs.p = nullptr; }
|
||||
|
||||
~minig() {
|
||||
if (p)
|
||||
p.destroy();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit minig(promise_type *p)
|
||||
: p(coroutine_handle<promise_type>::from_promise(*p)) {}
|
||||
|
||||
coroutine_handle<promise_type> p;
|
||||
};
|
||||
|
||||
|
||||
minig mini_count(int n) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
co_yield i;
|
||||
}
|
||||
}
|
||||
|
||||
generator<int> count(int n) {
|
||||
for (int i = 0; i < n; ++i)
|
||||
co_yield i;
|
||||
}
|
||||
|
||||
generator<int> range(int from, int n) {
|
||||
for (int i = from; i < n; ++i)
|
||||
co_yield i;
|
||||
}
|
||||
|
||||
void test_count() {
|
||||
const std::vector<int> expect = {0, 1, 2, 3, 4};
|
||||
std::vector<int> got;
|
||||
for (auto x : count(5))
|
||||
got.push_back(x);
|
||||
assert(expect == got);
|
||||
}
|
||||
|
||||
void test_range() {
|
||||
int sum = 0;
|
||||
for (auto v: range(1, 20))
|
||||
sum += v;
|
||||
assert(sum == 190);
|
||||
}
|
||||
|
||||
void test_mini_generator() {
|
||||
int sum = 0;
|
||||
auto g = mini_count(5);
|
||||
while (g.move_next()) {
|
||||
sum += g.current_value();
|
||||
}
|
||||
assert(sum == 10);
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_count();
|
||||
test_range();
|
||||
test_mini_generator();
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
// REQUIRES: fcoroutines-ts
|
||||
|
||||
// RUN: %build -fcoroutines-ts
|
||||
// RUN: %run
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <cassert>
|
||||
|
||||
using namespace std::experimental;
|
||||
|
||||
bool cancel = false;
|
||||
|
||||
struct goroutine
|
||||
{
|
||||
static int const N = 10;
|
||||
static int count;
|
||||
static coroutine_handle<> stack[N];
|
||||
|
||||
static void schedule(coroutine_handle<>& rh)
|
||||
{
|
||||
assert(count < N);
|
||||
stack[count++] = rh;
|
||||
rh = nullptr;
|
||||
}
|
||||
|
||||
~goroutine() {}
|
||||
|
||||
static void go(goroutine) {}
|
||||
|
||||
static void run_one()
|
||||
{
|
||||
assert(count > 0);
|
||||
stack[--count]();
|
||||
}
|
||||
|
||||
struct promise_type
|
||||
{
|
||||
suspend_never initial_suspend() {
|
||||
return {};
|
||||
}
|
||||
suspend_never final_suspend() {
|
||||
return {};
|
||||
}
|
||||
void return_void() {}
|
||||
goroutine get_return_object() {
|
||||
return{};
|
||||
}
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
};
|
||||
int goroutine::count;
|
||||
coroutine_handle<> goroutine::stack[N];
|
||||
|
||||
coroutine_handle<goroutine::promise_type> workaround;
|
||||
|
||||
class channel;
|
||||
|
||||
struct push_awaiter {
|
||||
channel* ch;
|
||||
bool await_ready() {return false; }
|
||||
void await_suspend(coroutine_handle<> rh);
|
||||
void await_resume() {}
|
||||
};
|
||||
|
||||
struct pull_awaiter {
|
||||
channel * ch;
|
||||
|
||||
bool await_ready();
|
||||
void await_suspend(coroutine_handle<> rh);
|
||||
int await_resume();
|
||||
};
|
||||
|
||||
class channel
|
||||
{
|
||||
using T = int;
|
||||
|
||||
friend struct push_awaiter;
|
||||
friend struct pull_awaiter;
|
||||
|
||||
T const* pvalue = nullptr;
|
||||
coroutine_handle<> reader = nullptr;
|
||||
coroutine_handle<> writer = nullptr;
|
||||
public:
|
||||
push_awaiter push(T const& value)
|
||||
{
|
||||
assert(pvalue == nullptr);
|
||||
assert(!writer);
|
||||
pvalue = &value;
|
||||
|
||||
return { this };
|
||||
}
|
||||
|
||||
pull_awaiter pull()
|
||||
{
|
||||
assert(!reader);
|
||||
|
||||
return { this };
|
||||
}
|
||||
|
||||
void sync_push(T const& value)
|
||||
{
|
||||
assert(!pvalue);
|
||||
pvalue = &value;
|
||||
assert(reader);
|
||||
reader();
|
||||
assert(!pvalue);
|
||||
reader = nullptr;
|
||||
}
|
||||
|
||||
auto sync_pull()
|
||||
{
|
||||
while (!pvalue) goroutine::run_one();
|
||||
auto result = *pvalue;
|
||||
pvalue = nullptr;
|
||||
if (writer)
|
||||
{
|
||||
auto wr = writer;
|
||||
writer = nullptr;
|
||||
wr();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
void push_awaiter::await_suspend(coroutine_handle<> rh)
|
||||
{
|
||||
ch->writer = rh;
|
||||
if (ch->reader) goroutine::schedule(ch->reader);
|
||||
}
|
||||
|
||||
|
||||
bool pull_awaiter::await_ready() {
|
||||
return !!ch->writer;
|
||||
}
|
||||
void pull_awaiter::await_suspend(coroutine_handle<> rh) {
|
||||
ch->reader = rh;
|
||||
}
|
||||
int pull_awaiter::await_resume() {
|
||||
auto result = *ch->pvalue;
|
||||
ch->pvalue = nullptr;
|
||||
if (ch->writer) {
|
||||
//goroutine::schedule(ch->writer);
|
||||
auto wr = ch->writer;
|
||||
ch->writer = nullptr;
|
||||
wr();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
goroutine pusher(channel& left, channel& right)
|
||||
{
|
||||
for (;;) {
|
||||
auto val = co_await left.pull();
|
||||
co_await right.push(val + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const int N = 100; //100'000'000;
|
||||
const int repeat = 1;
|
||||
|
||||
channel* c = new channel[N + 1];
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < N; ++i)
|
||||
goroutine::go(pusher(c[i], c[i + 1]));
|
||||
|
||||
c[0].sync_push(0);
|
||||
int result = c[N].sync_pull();
|
||||
|
||||
assert(result == 100);
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
// REQUIRES: fcoroutines-ts
|
||||
|
||||
// RUN: %build -fcoroutines-ts
|
||||
// RUN: %run
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <cassert>
|
||||
|
||||
using namespace std::experimental;
|
||||
|
||||
// This file tests, multishot, movable std::function like thing using coroutine
|
||||
// for compile-time type erasure and unerasure.
|
||||
template <typename R> struct func {
|
||||
struct Input {R a, b;};
|
||||
|
||||
struct promise_type {
|
||||
Input* I;
|
||||
R result;
|
||||
func get_return_object() { return {this}; }
|
||||
suspend_always initial_suspend() { return {}; }
|
||||
suspend_never final_suspend() { return {}; }
|
||||
void return_void() {}
|
||||
template <typename F>
|
||||
suspend_always yield_value(F&& f) {
|
||||
result = f(I->a, I->b);
|
||||
return {};
|
||||
}
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
|
||||
R operator()(Input I) {
|
||||
h.promise().I = &I;
|
||||
h.resume();
|
||||
R result = h.promise().result;
|
||||
return result;
|
||||
};
|
||||
|
||||
func() {}
|
||||
func(func &&rhs) : h(rhs.h) { rhs.h = nullptr; }
|
||||
func(func const &) = delete;
|
||||
|
||||
func &operator=(func &&rhs) {
|
||||
if (this != &rhs) {
|
||||
if (h)
|
||||
h.destroy();
|
||||
h = rhs.h;
|
||||
rhs.h = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename F> static func Create(F f) {
|
||||
for (;;) {
|
||||
co_yield f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F> func(F f) : func(Create(f)) {}
|
||||
|
||||
~func() {
|
||||
if (h)
|
||||
h.destroy();
|
||||
}
|
||||
|
||||
private:
|
||||
func(promise_type *promise)
|
||||
: h(coroutine_handle<promise_type>::from_promise(*promise)) {}
|
||||
coroutine_handle<promise_type> h;
|
||||
};
|
||||
|
||||
int Do(int acc, int n, func<int> f) {
|
||||
for (int i = 0; i < n; ++i)
|
||||
acc = f({acc, i});
|
||||
return acc;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int result = Do(1, 10, [](int a, int b) {return a + b;});
|
||||
assert(result == 46);
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11
|
||||
// REQUIRES: fcoroutines-ts
|
||||
|
||||
// RUN: %build -fcoroutines-ts
|
||||
// RUN: %run
|
||||
|
||||
#include <experimental/coroutine>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
using namespace std::experimental;
|
||||
|
||||
// This file tests, one shot, movable std::function like thing using coroutine
|
||||
// for compile-time type erasure and unerasure.
|
||||
|
||||
template <typename R> struct func {
|
||||
struct promise_type {
|
||||
R result;
|
||||
func get_return_object() { return {this}; }
|
||||
suspend_always initial_suspend() { return {}; }
|
||||
suspend_always final_suspend() { return {}; }
|
||||
void return_value(R v) { result = v; }
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
|
||||
R operator()() {
|
||||
h.resume();
|
||||
R result = h.promise().result;
|
||||
h.destroy();
|
||||
h = nullptr;
|
||||
return result;
|
||||
};
|
||||
|
||||
func() {}
|
||||
func(func &&rhs) : h(rhs.h) { rhs.h = nullptr; }
|
||||
func(func const &) = delete;
|
||||
|
||||
func &operator=(func &&rhs) {
|
||||
if (this != &rhs) {
|
||||
if (h)
|
||||
h.destroy();
|
||||
h = rhs.h;
|
||||
rhs.h = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename F> static func Create(F f) { co_return f(); }
|
||||
|
||||
template <typename F> func(F f) : func(Create(f)) {}
|
||||
|
||||
~func() {
|
||||
if (h)
|
||||
h.destroy();
|
||||
}
|
||||
|
||||
private:
|
||||
func(promise_type *promise)
|
||||
: h(coroutine_handle<promise_type>::from_promise(*promise)) {}
|
||||
coroutine_handle<promise_type> h;
|
||||
};
|
||||
|
||||
std::vector<int> yielded_values = {};
|
||||
int yield(int x) { yielded_values.push_back(x); return x + 1; }
|
||||
float fyield(int x) { yielded_values.push_back(x); return x + 2; }
|
||||
|
||||
void Do1(func<int> f) { yield(f()); }
|
||||
void Do2(func<double> f) { yield(f()); }
|
||||
|
||||
int main() {
|
||||
Do1([] { return yield(43); });
|
||||
assert((yielded_values == std::vector<int>{43, 44}));
|
||||
|
||||
yielded_values = {};
|
||||
Do2([] { return fyield(44); });
|
||||
assert((yielded_values == std::vector<int>{44, 46}));
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SUPPORT_COROUTINE_TYPES_H
|
||||
#define SUPPORT_COROUTINE_TYPES_H
|
||||
|
||||
#include <experimental/coroutine>
|
||||
|
||||
template <typename Ty> struct generator {
|
||||
struct promise_type {
|
||||
Ty current_value;
|
||||
std::experimental::suspend_always yield_value(Ty value) {
|
||||
this->current_value = value;
|
||||
return {};
|
||||
}
|
||||
std::experimental::suspend_always initial_suspend() { return {}; }
|
||||
std::experimental::suspend_always final_suspend() { return {}; }
|
||||
generator get_return_object() { return generator{this}; };
|
||||
void return_void() {}
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
|
||||
struct iterator {
|
||||
std::experimental::coroutine_handle<promise_type> _Coro;
|
||||
bool _Done;
|
||||
|
||||
iterator(std::experimental::coroutine_handle<promise_type> Coro, bool Done)
|
||||
: _Coro(Coro), _Done(Done) {}
|
||||
|
||||
iterator &operator++() {
|
||||
_Coro.resume();
|
||||
_Done = _Coro.done();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(iterator const &_Right) const {
|
||||
return _Done == _Right._Done;
|
||||
}
|
||||
|
||||
bool operator!=(iterator const &_Right) const { return !(*this == _Right); }
|
||||
|
||||
Ty const &operator*() const { return _Coro.promise().current_value; }
|
||||
|
||||
Ty const *operator->() const { return &(operator*()); }
|
||||
};
|
||||
|
||||
iterator begin() {
|
||||
p.resume();
|
||||
return {p, p.done()};
|
||||
}
|
||||
|
||||
iterator end() { return {p, true}; }
|
||||
|
||||
generator(generator &&rhs) : p(rhs.p) { rhs.p = nullptr; }
|
||||
|
||||
~generator() {
|
||||
if (p)
|
||||
p.destroy();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit generator(promise_type *p)
|
||||
: p(std::experimental::coroutine_handle<promise_type>::from_promise(*p)) {}
|
||||
|
||||
std::experimental::coroutine_handle<promise_type> p;
|
||||
};
|
||||
|
||||
#endif // SUPPORT_COROUTINE_TYPES_H
|
Loading…
Reference in New Issue