【C++20】第三方协程库测试

协程库:https://godbolt.org/z/icfqLr

说明: 以下代码在Ubuntu18.04 + GCC10上编译,GCC10构建方法参看【C++20】GCC10.1构建经验

coroutines.hpp

#ifndef COROUTINE_HPP
#define COROUTINE_HPP

#if __has_include(<coroutine>)
#include <coroutine>
#else
#include <experimental/coroutine>
namespace std {
    using std::experimental::suspend_always;
    using std::experimental::suspend_never;
    using std::experimental::noop_coroutine;
    using std::experimental::coroutine_handle;
}
#endif
#if __has_include(<ranges>)
#include <ranges>
#endif

#include <type_traits>
#include <iterator>
#include <cstdio>
#include <exception>
#include <utility>

template<typename Ref, typename Value = std::remove_cvref_t<Ref>>
class generator {
public:
    class promise_type {
    public:
        promise_type() : root_(this) {}
        generator get_return_object() noexcept {
            return generator{std::coroutine_handle<promise_type>::from_promise(*this)};
        }

        void unhandled_exception() {
            if (exception_ == nullptr)
                throw;
            *exception_ = std::current_exception();
        }

        void return_void() noexcept {}

        std::suspend_always initial_suspend() noexcept { return {}; }

        // Transfers control back to the parent of a nested coroutine
        struct final_awaiter {
            bool await_ready() noexcept {
                return false;
            }
            std::coroutine_handle<> await_suspend(
                std::coroutine_handle<promise_type> h) noexcept {
                auto& promise  = h.promise();
                auto  parent   = h.promise().parent_;
                if (parent) {
                    promise.root_->leaf_ = parent;
                    return std::coroutine_handle<promise_type>::from_promise(*parent);
                }
                return std::noop_coroutine();
            }
            void await_resume() noexcept {}
        };

        final_awaiter final_suspend() noexcept { return {}; }

        std::suspend_always yield_value(Ref&& x) noexcept {
            root_->value_ = std::addressof(x);
            return {};
        }
        std::suspend_always yield_value(Ref& x) noexcept {
            root_->value_ = std::addressof(x);
            return {};
        }

        struct yield_sequence_awaiter {
            generator gen_;
            std::exception_ptr exception_;

            explicit yield_sequence_awaiter(generator&& g) noexcept
            // Taking ownership of the generator ensures frame are destroyed
            // in the reverse order of their creation
            : gen_(std::move(g))
            {}

            bool await_ready() noexcept {
                return !gen_.coro_;
            }

            // set the parent, root and exceptions pointer and
            // resume the nested coroutine
            std::coroutine_handle<> await_suspend(
                std::coroutine_handle<promise_type> h) noexcept {
                auto& current = h.promise();
                auto& nested  = gen_.coro_.promise();
                auto& root    = current.root_;

                nested.root_   = root;
                root->leaf_    = &nested;
                nested.parent_ = &current;

                nested.exception_  = &exception_;

                // Immediately resume the nested coroutine (nested generator)
                return gen_.coro_;
            }

            void await_resume() {
                if (exception_) {
                    std::rethrow_exception(std::move(exception_));
                }
            }
        };

        yield_sequence_awaiter yield_value(generator&& g) noexcept {
            return yield_sequence_awaiter{std::move(g)};
        }

        void resume() {
            std::coroutine_handle<promise_type>::from_promise(*leaf_).resume();
        }

        // Disable use of co_await within this coroutine.
        void await_transform() = delete;

    private:
        friend generator;

        // Technically UB, for demonstration purpose
        union {
            promise_type* root_;
            promise_type* leaf_;
        };
        promise_type* parent_ = nullptr;
        std::exception_ptr* exception_ = nullptr;
        std::add_pointer_t<Ref> value_;
    };

    generator() noexcept = default;

    generator(generator&& other) noexcept
    : coro_(std::exchange(other.coro_, {}))
    {}

    ~generator() noexcept {
        if (coro_) {
            coro_.destroy();
        }
    }

    generator& operator=(generator g) noexcept {
        swap(g);
        return *this;
    }

    void swap(generator& other) noexcept {
        std::swap(coro_, other.coro_);
    }

    struct sentinel {};

    class iterator {
        using coroutine_handle = std::coroutine_handle<promise_type>;

      public:
        using iterator_category = std::input_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = Value;
        using reference =  Ref;
        using pointer =    std::add_pointer_t<Ref>;

        iterator() noexcept = default;
        iterator(const iterator &) = delete;
        iterator(iterator && o) {
            std::swap(coro_, o.coro_);
        }

        iterator &operator=(iterator && o) {
            std::swap(coro_, o.coro_);
            return *this;
        }

        ~iterator() {}

        friend bool operator==(const iterator &it, sentinel) noexcept {
            return !it.coro_ || it.coro_.done();
        }

        iterator &operator++() {
            coro_.promise().resume();
            return *this;
        }
        void operator++(int) {
            (void)operator++();
        }

        reference operator*() const noexcept {
            return static_cast<reference>(*coro_.promise().value_);
        }

        pointer operator->() const noexcept
        requires std::is_reference_v<reference> {
            return std::addressof(operator*());
        }

      private:
        friend generator;

        explicit iterator(coroutine_handle coro) noexcept
            : coro_(coro) {}

        coroutine_handle coro_;
    };

    iterator begin() {
        if (coro_) {
            coro_.resume();
        }
        return iterator{coro_};
    }

    sentinel end() noexcept {
        return {};
    }

private:
    explicit generator(std::coroutine_handle<promise_type> coro) noexcept
    : coro_(coro)
    {}

    std::coroutine_handle<promise_type> coro_;
};

#if __has_include(<ranges>)
template <typename T, typename U>
constexpr inline bool std::ranges::enable_view<generator<T, U>> = true;
#endif

#endif // COROUTINE_HPP


测试代码1:  

#include <iostream>
#include <filesystem>  // C++17
#include "coroutine.hpp"

using namespace std;

generator<filesystem::directory_entry> list_directory(filesystem::path path)
{
    for(auto entry: filesystem::directory_iterator(path))
    {
        co_yield entry;
        if(is_directory(entry))
            co_yield list_directory(entry);
    }
}

int main()
{
    auto files = list_directory(filesystem::path("."));
    for(auto f: files)
        std::cout<<f.path()<<"\n";
}

构建:

g++10 -std=c++2a -g -fcoroutines -o main main.cpp -I/usr/local/gcc-10.1.0/include/c++/10.1.0 -Wl,-rpath=/usr/local/gcc-10.1.0/lib64

测试代码2:

#include <iostream>
#include <cstdio>
#include "coroutine.hpp"

struct some_resource {
    int x;
    explicit some_resource(int x) : x(x) {
        std::printf("some_resource(%i)\n", x);
    }
    ~some_resource() {
        std::printf("~some_resource(%i)\n", x);
    }
};

static generator<const uint64_t> fib(int max) {
    some_resource r{2};
    auto a = 0, b = 1 ;
    for(auto n = 0; n <  max; n ++)  {
        co_yield b;
        const auto next = a + b;
        a = b, b = next;
    }
}


static generator<const uint64_t> test(int max) {
    auto g = fib(max);
    some_resource r{1};
    co_yield std::move(g);
}

int main() {
    setbuf(stdout, NULL);

#if __has_include(<ranges>)
    auto v = test(10) | std::views::drop(9);
    return *std::ranges::begin(v);
#else
    uint64_t  i = 0;
    for(auto a : test(10)) {
        i = a;
    }
    return i;
#endif
}

构建:

g++10 -std=c++2a -g -fcoroutines -o main main.cpp -I/usr/local/gcc-10.1.0/include/c++/10.1.0 -Wl,-rpath=/usr/local/gcc-10.1.0/lib64

猜你喜欢

转载自blog.csdn.net/qq2399431200/article/details/108326833