c++20 coroutine example
coroutine 作为C++20的新特性,在编程有着重要的应用。以下我们示范了如何使用coroutine,resumeable 类型和co_await able类型如何编写,以及coroutine中callee 和caller如何手动切换上下文。
#include <coroutine>
#include <iostream>
#include <type_traits>
#include <utility>
#include <optional>
#include <string_view>
#include <type_traits>
template <class T>
struct resumable_thing {
template <class U>
struct promise_type_impl_base ;
template <template<class >class F,class G>
struct promise_type_impl_base <F<G>>{
using U = F<G>;
using myresumeable =resumable_thing<G>;
constexpr myresumeable get_return_object() noexcept
{
return myresumeable(std::coroutine_handle<U>::from_promise(*(static_cast<U*>(this))));
}
constexpr auto initial_suspend() const noexcept { return std::suspend_never{}; }
auto final_suspend() const noexcept
{
std::cout << "final syspend\n";
return std::suspend_always{};
}
};
template <class U>
struct promise_type_impl : promise_type_impl_base<promise_type_impl<U>> {
using type = U;
std::optional<U> _value;
constexpr void return_value(U&& value) noexcept { _value = std::move(value); }
};
template <>
struct promise_type_impl<void> : promise_type_impl_base<promise_type_impl<void>> {
using type = void;
constexpr void return_void() noexcept {}
};
using promise_type = promise_type_impl<T>;
constexpr explicit resumable_thing(std::coroutine_handle<promise_type> coroutine) noexcept : _coroutine(coroutine) {}
constexpr const auto& get() const noexcept requires !std::is_void_v<T> { return _coroutine.promise()._value; }
// constexpr void get() const noexcept requires std::is_void_v<T> {}
~resumable_thing() noexcept
{
if (_coroutine) {
_coroutine.destroy();
}
}
constexpr void resume() const noexcept { _coroutine.resume(); }
constexpr resumable_thing() = default;
constexpr resumable_thing(resumable_thing const&) = delete;
constexpr resumable_thing& operator=(resumable_thing const&) = delete;
constexpr resumable_thing(resumable_thing&& other) noexcept : _coroutine(other._coroutine)
{
other._coroutine = nullptr;
}
constexpr resumable_thing& operator=(resumable_thing&& other) & noexcept
{
if (&other != this) {
_coroutine = other._coroutine;
other._coroutine = nullptr;
}
return *this;
}
std::coroutine_handle<promise_type> _coroutine = nullptr;
};
struct Trace {
std::string func_;
Trace(const Trace& other)
{
func_ = other.func_;
std::cout << func_ << " copy constructor\n";
}
Trace& operator=(const Trace& other)
{
func_ = other.func_;
std::cout << func_ << " assign operator\n";
return *this;
}
~Trace() { std::cout << func_ << " Deconstruct\n"; }
Trace(std::string_view f) : func_(f) { std::cout << f << " Construct\n"; }
};
struct Task {
~Task() { std::cout << "task descontructor\n"; }
int i = 0;
constexpr bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> h) const noexcept
{
h.resume();
std::cout << "Task await_suspend \n";
}
int await_resume() const noexcept
{
std::cout << "Task await_resume \n";
return 42;
}
};
resumable_thing<void> f_void() noexcept
{
Trace a("f_void");
auto i = co_await Task{};
std::cout << i << std::endl;
co_return;
}
resumable_thing<int> f_int() noexcept
{
co_await Task{};
co_return 1;
}
resumable_thing<int> f_suspend_always(Trace s) noexcept
{
// Trace a("f_suspend_always ");
co_await std::suspend_always{};
// std::cout << "f_suspend_always Step A\n";
co_return 500;
}
resumable_thing<int> f_suspend_never() noexcept
{
Trace a("f_suspend_never");
co_await std::suspend_never{};
std::cout << "suspend_never 1\n";
co_await std::suspend_never{};
std::cout << "suspend_never 2\n";
co_return 1;
}
int main()
{
//{
// std::cout << "Test A:\n";
// auto r = f_int();
// r.resume();
// if (auto& o = r.get(); o) std::cout << *o << std::endl;
//}
{
std::cout << "\nTest B:\n";
auto r = f_void();
std::cout << "\nTest B:\n";
// r.resume();
}
{
// std::cout << "\nTest C:\n";
// auto r = f_suspend_always(Trace("f_susPendParameter"));
// std::cout << "suspend_always get r before resume :";
// if (auto& o = r.get(); o) std::cout << *o << std::endl;
// r.resume();
// std::cout << "suspend_always get r after resume ";
// std::cout << *r.get() << std::endl;
}
//{
// std::cout << "\nTest D:\n";
// auto r = f_suspend_never();
// if (auto& o = r.get(); o) std::cout << *o << std::endl;
//}
}
Posted 2020-09-19