/* * From https://github.com/toby-allsopp/coroutine_monad * * Copyright (c) 2017 Toby Allsopp * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ module; #include #include #include #include #include export module nihil.monad; namespace nihil { /********************************************************************** * return_object_holder */ // An object that starts out unitialized. Initialized by a call to emplace. template using deferred = std::optional; template struct return_object_holder { // The staging object that is returned (by copy/move) to the caller of // the coroutine. deferred stage; return_object_holder*& p; // When constructed, we assign a pointer to ourselves to the supplied // reference to pointer. return_object_holder(return_object_holder*& p) : stage{} , p(p) { p = this; } // Copying doesn't make any sense (which copy should the pointer refer // to?). return_object_holder(return_object_holder const&) = delete; // To move, we just update the pointer to point at the new object. return_object_holder(return_object_holder&& other) : stage(std::move(other.stage)) , p(other.p) { p = this; } // Assignment doesn't make sense. void operator=(return_object_holder const&) = delete; void operator=(return_object_holder&&) = delete; // A non-trivial destructor is required until // https://bugs.llvm.org//show_bug.cgi?id=28593 is fixed. ~return_object_holder() {} // Construct the staging value; arguments are perfect forwarded to T's // constructor. template void emplace(Args&&... args) { stage.emplace(std::forward(args)...); } // We assume that we will be converted only once, so we can move from // the staging object. We also assume that `emplace` has been called // at least once. operator T() { return std::move(*stage); } }; template auto make_return_object_holder(return_object_holder*& p) { return return_object_holder{p}; } /********************************************************************** * std::optional */ template struct optional_promise { return_object_holder>* data; auto get_return_object() { return make_return_object_holder(data); } auto initial_suspend() noexcept -> std::suspend_never { return {}; } auto final_suspend() noexcept -> std::suspend_never { return {}; } void return_value(T x) { data->emplace(std::move(x)); } void unhandled_exception() { std::rethrow_exception(std::current_exception()); } }; } // namespace nihil export template struct std::coroutine_traits, Args...> { using promise_type = nihil::optional_promise; }; namespace nihil { template struct optional_awaitable { std::optional o; auto await_ready() { return o.has_value(); } auto await_resume() { return *o; } template void await_suspend(std::coroutine_handle> h) { h.promise().data->emplace(std::nullopt); h.destroy(); } }; } // namespace nihil namespace std { export template auto operator co_await(std::optional o) { return nihil::optional_awaitable{std::move(o)}; } } // namespace std /********************************************************************** * std::expected */ namespace nihil { export template struct expected_promise_base { return_object_holder>* data; auto get_return_object() { return make_return_object_holder(data); } auto initial_suspend() noexcept -> std::suspend_never { return {}; } auto final_suspend() noexcept -> std::suspend_never { return {}; } void unhandled_exception() { std::rethrow_exception(std::current_exception()); } }; export template struct expected_promise : expected_promise_base { void return_value(this expected_promise &self, std::unexpected err) { self.data->emplace(std::move(err)); } void return_value(this expected_promise &self, T o) { self.data->emplace(std::move(o)); } }; export template struct expected_promise : expected_promise_base { void return_value(this expected_promise &self, std::unexpected err) { self.data->emplace(std::move(err)); } void return_value(this expected_promise &self, std::expected o) { self.data->emplace(std::move(o)); } }; } // namespace nihil export template struct std::coroutine_traits, Args...> { using promise_type = nihil::expected_promise; }; namespace nihil { export template struct expected_awaitable_base { std::expected o; auto await_ready() { return o.has_value(); } template void await_suspend(std::coroutine_handle

h) { h.promise().data->emplace(std::unexpected(o.error())); h.destroy(); } }; export template struct expected_awaitable : expected_awaitable_base { auto await_resume(this expected_awaitable &self) { return std::move(*self.o); } }; export template struct expected_awaitable : expected_awaitable_base { auto await_resume(this expected_awaitable &) { return std::expected(); } }; } // namespace nihil namespace std { export template auto operator co_await(std::expected o) { return nihil::expected_awaitable{std::move(o)}; } } // namespace std