#pragma once #include #include #include #include #include #include #include #include #include #include #include namespace hysj::inline _HYSJ_VERSION_NAMESPACE::coroutines::builtin{ namespace trees{ template concept edible = !std::is_void_v; template concept inedible = !edible; template struct root; template struct seed{ using return_object = root; std::coroutine_handle<> soil = std::noop_coroutine(); struct seeding{ seed self; static constexpr bool await_ready()noexcept{ return false; } constexpr std::coroutine_handle<> await_suspend(auto soil)noexcept{ return self.soil = soil; } constexpr seed await_resume()noexcept{ return self; } }; seeding operator co_await()const noexcept{ return {*this}; } }; template struct ripening{ TreeReference branch; //FIXME: check if ripened - jeh constexpr static bool await_ready()noexcept{ return false; } std::coroutine_handle<> await_suspend(const auto &)noexcept{ return branch.trunk().ripening_handle; } constexpr auto await_resume()noexcept{ return branch.harvest(); } }; template ripening(Tree &&) -> ripening; //FIXME: // parametrize stem on cluster-size & suspension discipline. // - jeh template struct ripened{ Value value; static constexpr bool await_ready()noexcept{ return false; } constexpr std::coroutine_handle<> await_suspend(auto tree)noexcept{ auto &trunk = tree.promise().trunk; if(trunk.soil) return trunk.soil; return std::noop_coroutine(); } constexpr Value await_resume()const noexcept{ return value; } }; //FIXME: // stem doesn't need storage if unary and always suspending, could store reference to yielded // - jeh template struct stem; template struct stem{ using cluster_type = std::optional; cluster_type cluster{}; constexpr auto ripen(auto &&... flower){ cluster = Fruit{fwd(flower)...}; } constexpr auto pick()noexcept{ return std::exchange(cluster,std::nullopt).value(); } static constexpr cluster_type dearth()noexcept{ return std::nullopt; } }; template struct stem{ constexpr auto pick()noexcept{} constexpr auto ripen()const noexcept{} static constexpr auto dearth()noexcept{} }; template constexpr stem make_fruit_stem(seed)noexcept{ return {}; } template struct trunk{ std::coroutine_handle<> soil; [[no_unique_address]] stem fruit; std::coroutine_handle<> ripening_handle; }; template trunk make_trunk(seed seed_){ return {seed_.soil,make_fruit_stem(seed_),nullptr}; } struct initial_root_awaitable{ static constexpr bool await_ready()noexcept{ return false; } static constexpr void await_suspend(auto root_handle)noexcept{ auto &trunk = root_handle.promise().trunk; trunk.ripening_handle = root_handle; } static constexpr void await_resume()noexcept{} }; struct final_root_awaitable{ static constexpr bool await_ready()noexcept{ return false; } static constexpr std::coroutine_handle<> await_suspend(auto root_handle)noexcept{ auto &trunk = root_handle.promise().trunk; if(!trunk.soil) return std::noop_coroutine(); trunk.ripening_handle = std::coroutine_handle<>{}; return trunk.soil; } static constexpr void await_resume()noexcept{} }; template struct root_promise; template using root_handle = std::coroutine_handle>; template struct root_promise{ trees::trunk trunk; root_promise(seed &seed_): trunk{make_trunk(seed_)} { } root_promise(seed &seed_,const auto &...): root_promise{seed_} {} root_promise(const auto &,seed &seed_,const auto &...): root_promise{seed_} {} static constexpr initial_root_awaitable initial_suspend()noexcept{ return {}; } static constexpr final_root_awaitable final_suspend()noexcept{ return {}; } void return_void(){} static void unhandled_exception(){ throw; } auto to_handle(){ return root_handle::from_promise(*this); } auto get_return_object(){ return root{to_handle()}; } }; template struct root{ using promise_type = root_promise; root_handle handle; constexpr explicit root(root_handle handle): handle{handle} {} constexpr root() noexcept = default; constexpr root(root &&rhs) noexcept: handle{std::exchange(rhs.handle,nullptr)} {} constexpr root &operator=(root &&rhs){ if(this == &rhs) return *this; if(handle) chop(); handle = std::exchange(rhs.handle,nullptr); return *this; } ~root(){ if(handle) chop(); } auto &trunk(){ return handle.promise().trunk; } auto &trunk()const{ return handle.promise().trunk; } bool stump()const noexcept{ return !handle || handle.done(); } void ripen(){ trunk().ripening_handle.resume(); } auto harvest()const{ return trunk().fruit.pick(); } void chop(){ handle.destroy(); } explicit operator bool()const noexcept{ return !stump(); } auto operator co_await()&{ return ripening{*this}; } auto operator co_await()&&{ return ripening{std::move(*this)}; } auto operator()(){ if(stump()) return stem::dearth(); ripen(); return harvest(); } friend HYSJ_MIRROR_STRUCT_ID(root); }; //NOTE: don't need heap alloc, don't need shared_ptr - jeh template using vein = std::shared_ptr; template auto make_vein(std::shared_ptr f){ return f; } template auto make_vein(auto &&... arg){ return std::make_shared(fwd(arg)...); } template constexpr stem make_energy_stem(auto &)noexcept{ return {}; } template constexpr vein> make_energy_vein(auto &tree)noexcept{ return make_vein>(make_energy_stem(tree)); } template struct branch; template struct sprout{ using return_object = branch; trees::trunk *trunk; vein> energy; std::coroutine_handle<> bough; struct resprouter{ sprout self; static constexpr bool await_ready()noexcept{ return false; } template std::coroutine_handle<> await_suspend(Bough bough)noexcept{ self.trunk = std::addressof(bough.promise().trunk); self.bough = bough; return bough; } constexpr sprout await_resume()const noexcept{ return self; } }; resprouter operator co_await()const noexcept{ return {*this}; } }; template constexpr sprout get_proto_promise(std::type_identity,root_promise &parent){ return {std::addressof(parent.trunk),make_energy_vein(parent),parent.to_handle()}; } template auto yield(root_promise &root,Values &&...values){ root.trunk.fruit.ripen(fwd(values)...); return std::suspend_never{}; } template auto halt(root_promise &root,Values &&...values){ root.trunk.fruit.ripen(fwd(values)...); return std::suspend_always{}; } template struct bough{ vein> energy; std::coroutine_handle<> handle; }; template bough make_bough(trees::sprout sprout){ return {sprout.energy,sprout.bough}; } template struct branch_promise; template using branch_handle = std::coroutine_handle>; struct initial_branch_awaitable{ static constexpr bool await_ready()noexcept{ return false; } static constexpr void await_suspend(auto branch_handle)noexcept{ auto &branch = branch_handle.promise(); branch.trunk.ripening_handle = branch_handle; } static constexpr void await_resume()noexcept{} }; struct final_branch_awaitable{ static constexpr bool await_ready()noexcept{ return false; } static constexpr std::coroutine_handle<> await_suspend(auto branch_handle)noexcept{ auto &branch = branch_handle.promise(); branch.trunk.ripening_handle = branch.bough.handle; return branch.bough.handle; } static constexpr void await_resume()noexcept{} }; template struct branch_promise{ trees::trunk &trunk; trees::bough bough; branch_promise() = delete; branch_promise(trees::sprout sprout): trunk{*sprout.trunk}, bough{make_bough(sprout)} { } branch_promise(trees::sprout sprout,const auto &...): branch_promise{sprout} {} branch_promise(const auto &,trees::sprout sprout,const auto &...): branch_promise{sprout} {} auto to_handle(){ return branch_handle::from_promise(*this); } static constexpr initial_branch_awaitable initial_suspend()noexcept{ return {}; } static constexpr final_branch_awaitable final_suspend()noexcept{ return {}; } void return_void(){} static void unhandled_exception(){ throw; } auto get_return_object(){ return branch{to_handle()}; } template constexpr sprout get_proto_promise(std::type_identity, branch_promise &parent){ if constexpr(std::is_same_v){ return {std::addressof(parent.trunk),parent.energy,parent.to_handle()}; } else return {std::addressof(parent.trunk),make_energy_vein(parent),parent.to_handle()}; } }; template auto yield(branch_promise &branch,Values &&...values){ branch.bough.energy->ripen(fwd(values)...); return std::suspend_never{}; } template auto halt(branch_promise &branch,Values &&...values){ branch.trunk.fruit.ripen(fwd(values)...); return std::suspend_always{}; } template struct growth{ BranchReference branch; //FIXME: check if produced - jeh constexpr static bool await_ready()noexcept{ return false; } std::coroutine_handle<> await_suspend(const auto &)noexcept{ return branch.trunk().ripening_handle; } constexpr auto await_resume()noexcept{ return branch.bough().energy->pick(); } }; template growth(Branch &&) -> growth; template struct branch{ using promise_type = branch_promise; branch_handle handle; constexpr explicit branch(branch_handle handle): handle{handle} {} constexpr branch() noexcept = default; constexpr branch(branch &&rhs) noexcept: handle{std::exchange(rhs.handle,nullptr)} {} constexpr branch &operator=(branch &&rhs){ if(this == &rhs) return *this; if(handle) chop(); handle = std::exchange(rhs.handle,nullptr); return *this; } ~branch(){ if(handle) chop(); } auto &trunk(){ return handle.promise().trunk; } auto &trunk()const{ return handle.promise().trunk; } auto &bough(){ return handle.promise().bough; } auto &bough()const{ return handle.promise().bough; } bool stump()const noexcept{ return !handle || handle.done(); } void chop(){ handle.destroy(); } explicit operator bool()const noexcept{ return !stump(); } auto operator co_await()&{ return growth{*this}; } auto operator co_await()&&{ return growth{std::move(*this)}; } friend HYSJ_MIRROR_STRUCT_ID(branch); }; template constexpr sprout get_proto_promise(std::type_identity,branch_promise &parent){ return {std::addressof(parent.trunk),make_energy_vein(parent),parent.to_handle()}; } using sentinel = std::default_sentinel_t; template struct root_iterator{ using iterator_category = std::input_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = Fruit; using reference = std::add_lvalue_reference_t; using pointer = std::add_pointer_t; root *root_ptr = nullptr; root_iterator() noexcept = default; root_iterator(root_iterator &&rhs){ std::swap(root_ptr,rhs.root_ptr); } explicit root_iterator(root &root_ptr_)noexcept: root_ptr{std::addressof(root_ptr_)} {} constexpr root_iterator &operator=(root_iterator &&rhs)noexcept{ std::swap(root_ptr,rhs.root_ptr); return *this; } ~root_iterator(){} inline friend bool operator==(const root_iterator &it,sentinel)noexcept{ return !it.root_ptr || it.root_ptr->stump(); } root_iterator &operator++(){ root_ptr->ripen(); return *this; } void operator++(int){ (void)operator++(); } reference operator*()const noexcept{ return *(root_ptr->trunk().fruit.cluster); } pointer operator->()const noexcept{ return std::addressof(operator*()); } }; template root_iterator(root &) -> root_iterator ; template root_iterator begin(root &root_)noexcept{ if(root_) root_.ripen(); return root_iterator{root_}; } template constexpr sentinel end(root &)noexcept{ return {}; } } //trees template using co_seed = trees::seed; template using co_root = trees::root; template using co_sprout = trees::sprout; template using co_branch = trees::branch; } //hysj::coroutines::builtin #include