From a4607e29540a9352c35afff17193ceeab137cc9d Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Wed, 2 Jul 2025 03:25:28 +0100 Subject: move monad to util --- CMakeLists.txt | 1 - nihil.cli/CMakeLists.txt | 3 +- nihil.config/option.cc | 2 +- nihil.config/read.ccm | 2 +- nihil.config/store.cc | 2 +- nihil.config/string.ccm | 2 +- nihil.config/write.ccm | 2 +- nihil.error/CMakeLists.txt | 4 +- nihil.monad/CMakeLists.txt | 22 ---- nihil.monad/monad.ccm | 282 --------------------------------------------- nihil.monad/test.cc | 64 ---------- nihil.posix/CMakeLists.txt | 4 +- nihil.posix/execvp.ccm | 2 +- nihil.posix/fd.ccm | 1 - nihil.posix/read_file.ccm | 2 +- nihil.posix/spawn.ccm | 3 +- nihil.posix/write_file.ccm | 1 - nihil.ucl/CMakeLists.txt | 12 +- nihil.ucl/object_cast.ccm | 2 +- nihil.ucl/parser.ccm | 2 +- nihil.util/CMakeLists.txt | 5 +- nihil.util/monad.ccm | 282 +++++++++++++++++++++++++++++++++++++++++++++ nihil.util/monad.test.cc | 66 +++++++++++ nihil.util/nihil.util.ccm | 1 + nihil.util/parse_size.ccm | 2 +- 25 files changed, 382 insertions(+), 389 deletions(-) delete mode 100644 nihil.monad/CMakeLists.txt delete mode 100644 nihil.monad/monad.ccm delete mode 100644 nihil.monad/test.cc create mode 100644 nihil.util/monad.ccm create mode 100644 nihil.util/monad.test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c02a3e..d417178 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,6 @@ add_subdirectory(nihil.core) add_subdirectory(nihil.error) add_subdirectory(nihil.generator) add_subdirectory(nihil.match) -add_subdirectory(nihil.monad) add_subdirectory(nihil.posix) add_subdirectory(nihil.util) add_subdirectory(nihil.uuid) diff --git a/nihil.cli/CMakeLists.txt b/nihil.cli/CMakeLists.txt index 859fea5..22fc1de 100644 --- a/nihil.cli/CMakeLists.txt +++ b/nihil.cli/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(nihil.cli STATIC) target_link_libraries(nihil.cli PRIVATE nihil.std nihil.generator - nihil.posix; + nihil.posix nihil.util ) target_sources(nihil.cli @@ -30,6 +30,7 @@ if(NIHIL_TESTS) dispatch_command.test.cc ) target_link_libraries(nihil.cli.test PRIVATE + nihil.std nihil.cli Catch2::Catch2WithMain ) diff --git a/nihil.config/option.cc b/nihil.config/option.cc index 50c7a1f..acd9d24 100644 --- a/nihil.config/option.cc +++ b/nihil.config/option.cc @@ -3,8 +3,8 @@ module nihil.config; import nihil.std; import nihil.error; -import nihil.monad; import nihil.ucl; +import nihil.util; namespace nihil::config { diff --git a/nihil.config/read.ccm b/nihil.config/read.ccm index 7065492..c326fed 100644 --- a/nihil.config/read.ccm +++ b/nihil.config/read.ccm @@ -3,9 +3,9 @@ export module nihil.config:read; import nihil.std; import nihil.error; -import nihil.monad; import nihil.posix; import nihil.ucl; +import nihil.util; import :option; import :store; diff --git a/nihil.config/store.cc b/nihil.config/store.cc index e1ca271..d0842ec 100644 --- a/nihil.config/store.cc +++ b/nihil.config/store.cc @@ -4,7 +4,7 @@ module nihil.config; import nihil.std; import nihil.error; import nihil.generator; -import nihil.monad; +import nihil.util; namespace nihil::config { diff --git a/nihil.config/string.ccm b/nihil.config/string.ccm index 12ede7a..c5e7284 100644 --- a/nihil.config/string.ccm +++ b/nihil.config/string.ccm @@ -2,7 +2,7 @@ export module nihil.config:string; import nihil.std; -import nihil.monad; +import nihil.util; import nihil.ucl; import :option; diff --git a/nihil.config/write.ccm b/nihil.config/write.ccm index a7eddd5..89b9c93 100644 --- a/nihil.config/write.ccm +++ b/nihil.config/write.ccm @@ -3,9 +3,9 @@ export module nihil.config:write; import nihil.std; import nihil.error; -import nihil.monad; import nihil.posix; import nihil.ucl; +import nihil.util; import :option; import :store; diff --git a/nihil.error/CMakeLists.txt b/nihil.error/CMakeLists.txt index 8a215b7..81c2cd8 100644 --- a/nihil.error/CMakeLists.txt +++ b/nihil.error/CMakeLists.txt @@ -15,7 +15,9 @@ if(NIHIL_TESTS) error.test.cc) target_link_libraries(nihil.error.test PRIVATE - nihil.std nihil.core nihil.error + nihil.std + nihil.core + nihil.error Catch2::Catch2WithMain ) diff --git a/nihil.monad/CMakeLists.txt b/nihil.monad/CMakeLists.txt deleted file mode 100644 index f82e464..0000000 --- a/nihil.monad/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# This source code is released into the public domain. - -add_library(nihil.monad STATIC) -target_link_libraries(nihil.monad PRIVATE nihil.std nihil.error) -target_sources(nihil.monad - PUBLIC FILE_SET modules TYPE CXX_MODULES FILES - monad.ccm -) - -if(NIHIL_TESTS) - enable_testing() - - add_executable(nihil.monad.test test.cc) - target_link_libraries(nihil.monad.test PRIVATE - nihil.monad - Catch2::Catch2WithMain - ) - - include(CTest) - include(Catch) - catch_discover_tests(nihil.monad.test) -endif() diff --git a/nihil.monad/monad.ccm b/nihil.monad/monad.ccm deleted file mode 100644 index cd17e0f..0000000 --- a/nihil.monad/monad.ccm +++ /dev/null @@ -1,282 +0,0 @@ -/* - * 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. - */ - -export module nihil.monad; - -import nihil.std; - -namespace nihil { - -/********************************************************************** - * return_object_holder - */ - -// An object that starts out unitialized. Initialized by a call to emplace. -export template -using deferred = std::optional; - -export 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); - } -}; - -export 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 diff --git a/nihil.monad/test.cc b/nihil.monad/test.cc deleted file mode 100644 index 2cc743c..0000000 --- a/nihil.monad/test.cc +++ /dev/null @@ -1,64 +0,0 @@ -// This source code is released into the public domain. - -#include - -import nihil.std; -import nihil.error; -import nihil.monad; - -TEST_CASE("monad: co_await std::optional<> with value", "[nihil]") -{ - auto get_value = [] -> std::optional { - return 42; - }; - - auto try_get_value = [&get_value] -> std::optional { - co_return co_await get_value(); - }; - - auto o = try_get_value(); - REQUIRE(o == 42); -} - -TEST_CASE("monad: co_await std::optional<> without value", "[nihil]") -{ - auto get_value = [] -> std::optional { - return {}; - }; - - auto try_get_value = [&get_value] -> std::optional { - co_return co_await get_value(); - }; - - auto o = try_get_value(); - REQUIRE(!o.has_value()); -} - -TEST_CASE("monad: co_await std::expected<> with value", "[nihil]") -{ - auto get_value = [] -> std::expected { - return 42; - }; - - auto try_get_value = [&get_value] -> std::expected { - co_return co_await get_value(); - }; - - auto o = try_get_value(); - REQUIRE(o == 42); -} - -TEST_CASE("monad: co_await std::expected<> with error", "[nihil]") -{ - auto get_value = [] -> std::expected { - return std::unexpected("error"); - }; - - auto try_get_value = [&get_value] -> std::expected { - co_return co_await get_value(); - }; - - auto o = try_get_value(); - REQUIRE(!o); - REQUIRE(o.error() == "error"); -} diff --git a/nihil.posix/CMakeLists.txt b/nihil.posix/CMakeLists.txt index c4a3556..a4c174b 100644 --- a/nihil.posix/CMakeLists.txt +++ b/nihil.posix/CMakeLists.txt @@ -5,7 +5,6 @@ target_link_libraries(nihil.posix PRIVATE nihil.std nihil.core nihil.error - nihil.monad nihil.util ) @@ -59,7 +58,10 @@ if(NIHIL_TESTS) ) target_link_libraries(nihil.posix.test PRIVATE + nihil.std + nihil.error nihil.posix + nihil.util Catch2::Catch2WithMain ) diff --git a/nihil.posix/execvp.ccm b/nihil.posix/execvp.ccm index 14e548e..5b11e7a 100644 --- a/nihil.posix/execvp.ccm +++ b/nihil.posix/execvp.ccm @@ -4,7 +4,7 @@ export module nihil.posix:execvp; import nihil.std; import nihil.core; import nihil.error; -import nihil.monad; +import nihil.util; import :argv; import :execv; import :find_in_path; diff --git a/nihil.posix/fd.ccm b/nihil.posix/fd.ccm index 40bfb0a..c00676d 100644 --- a/nihil.posix/fd.ccm +++ b/nihil.posix/fd.ccm @@ -8,7 +8,6 @@ export module nihil.posix:fd; import nihil.std; import nihil.error; -import nihil.monad; import nihil.util; namespace nihil { diff --git a/nihil.posix/read_file.ccm b/nihil.posix/read_file.ccm index 61c5085..337d0e4 100644 --- a/nihil.posix/read_file.ccm +++ b/nihil.posix/read_file.ccm @@ -2,7 +2,7 @@ export module nihil.posix:read_file; import nihil.error; -import nihil.monad; +import nihil.util; import :fd; import :open; diff --git a/nihil.posix/spawn.ccm b/nihil.posix/spawn.ccm index 1e4102a..ea84f4d 100644 --- a/nihil.posix/spawn.ccm +++ b/nihil.posix/spawn.ccm @@ -3,7 +3,8 @@ export module nihil.posix:spawn; // spawn(): fork and execute a child process. -import nihil.monad; +import nihil.std; +import nihil.util; import :argv; import :executor; import :fd; diff --git a/nihil.posix/write_file.ccm b/nihil.posix/write_file.ccm index ad1129f..e706274 100644 --- a/nihil.posix/write_file.ccm +++ b/nihil.posix/write_file.ccm @@ -3,7 +3,6 @@ export module nihil.posix:write_file; import nihil.std; import nihil.error; -import nihil.monad; import nihil.util; import :fd; import :open; diff --git a/nihil.ucl/CMakeLists.txt b/nihil.ucl/CMakeLists.txt index 5b8ed72..f0e7ec0 100644 --- a/nihil.ucl/CMakeLists.txt +++ b/nihil.ucl/CMakeLists.txt @@ -3,7 +3,12 @@ pkg_check_modules(LIBUCL REQUIRED libucl) add_library(nihil.ucl STATIC) -target_link_libraries(nihil.ucl PRIVATE nihil.std nihil.core nihil.error nihil.monad) +target_link_libraries(nihil.ucl PRIVATE + nihil.std + nihil.core + nihil.util + nihil.error +) target_sources(nihil.ucl PUBLIC FILE_SET modules TYPE CXX_MODULES FILES @@ -40,7 +45,10 @@ if(NIHIL_TESTS) string.test.cc ) - target_link_libraries(nihil.ucl.test PRIVATE nihil.ucl Catch2::Catch2WithMain) + target_link_libraries(nihil.ucl.test PRIVATE + nihil.std + nihil.ucl + Catch2::Catch2WithMain) include(CTest) include(Catch) diff --git a/nihil.ucl/object_cast.ccm b/nihil.ucl/object_cast.ccm index 5a09085..04d75a9 100644 --- a/nihil.ucl/object_cast.ccm +++ b/nihil.ucl/object_cast.ccm @@ -6,7 +6,7 @@ module; export module nihil.ucl:object_cast; import nihil.std; -import nihil.monad; +import nihil.util; import :type; import :object; import :array; diff --git a/nihil.ucl/parser.ccm b/nihil.ucl/parser.ccm index 0100fda..306f068 100644 --- a/nihil.ucl/parser.ccm +++ b/nihil.ucl/parser.ccm @@ -6,7 +6,7 @@ module; export module nihil.ucl:parser; import nihil.std; -import nihil.monad; +import nihil.util; import :object; import :map; diff --git a/nihil.util/CMakeLists.txt b/nihil.util/CMakeLists.txt index 2755103..4c832c8 100644 --- a/nihil.util/CMakeLists.txt +++ b/nihil.util/CMakeLists.txt @@ -5,7 +5,6 @@ target_link_libraries(nihil.util PRIVATE nihil.std nihil.core nihil.error - nihil.monad ) target_sources(nihil.util PUBLIC FILE_SET modules TYPE CXX_MODULES FILES @@ -16,6 +15,7 @@ target_sources(nihil.util ctype.ccm flagset.ccm guard.ccm + monad.ccm parse_size.ccm next_word.ccm save_errno.ccm @@ -31,13 +31,14 @@ if(NIHIL_TESTS) ctype.test.cc flagset.test.cc guard.test.cc + monad.test.cc parse_size.test.cc next_word.test.cc skipws.test.cc tabulate.test.cc ) target_link_libraries(nihil.util.test PRIVATE - nihil.util + nihil.std nihil.util Catch2::Catch2WithMain ) diff --git a/nihil.util/monad.ccm b/nihil.util/monad.ccm new file mode 100644 index 0000000..eefa1fe --- /dev/null +++ b/nihil.util/monad.ccm @@ -0,0 +1,282 @@ +/* + * 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. + */ + +export module nihil.util:monad; + +import nihil.std; + +namespace nihil { + +/********************************************************************** + * return_object_holder + */ + +// An object that starts out unitialized. Initialized by a call to emplace. +export template +using deferred = std::optional; + +export 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); + } +}; + +export 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 diff --git a/nihil.util/monad.test.cc b/nihil.util/monad.test.cc new file mode 100644 index 0000000..bc9e406 --- /dev/null +++ b/nihil.util/monad.test.cc @@ -0,0 +1,66 @@ +// This source code is released into the public domain. + +#include + +import nihil.std; +import nihil.error; +import nihil.util; + +namespace { +TEST_CASE("monad: co_await std::optional<> with value", "[nihil]") +{ + auto get_value = [] -> std::optional { + return 42; + }; + + auto try_get_value = [&get_value] -> std::optional { + co_return co_await get_value(); + }; + + auto o = try_get_value(); + REQUIRE(o == 42); +} + +TEST_CASE("monad: co_await std::optional<> without value", "[nihil]") +{ + auto get_value = [] -> std::optional { + return {}; + }; + + auto try_get_value = [&get_value] -> std::optional { + co_return co_await get_value(); + }; + + auto o = try_get_value(); + REQUIRE(!o.has_value()); +} + +TEST_CASE("monad: co_await std::expected<> with value", "[nihil]") +{ + auto get_value = [] -> std::expected { + return 42; + }; + + auto try_get_value = [&get_value] -> std::expected { + co_return co_await get_value(); + }; + + auto o = try_get_value(); + REQUIRE(o == 42); +} + +TEST_CASE("monad: co_await std::expected<> with error", "[nihil]") +{ + auto get_value = [] -> std::expected { + return std::unexpected("error"); + }; + + auto try_get_value = [&get_value] -> std::expected { + co_return co_await get_value(); + }; + + auto o = try_get_value(); + REQUIRE(!o); + REQUIRE(o.error() == "error"); +} +} // anonymous namespace diff --git a/nihil.util/nihil.util.ccm b/nihil.util/nihil.util.ccm index 6b4bc30..de88f8c 100644 --- a/nihil.util/nihil.util.ccm +++ b/nihil.util/nihil.util.ccm @@ -6,6 +6,7 @@ export import :construct; export import :ctype; export import :flagset; export import :guard; +export import :monad; export import :parse_size; export import :next_word; export import :save_errno; diff --git a/nihil.util/parse_size.ccm b/nihil.util/parse_size.ccm index 7fc3fa4..6219323 100644 --- a/nihil.util/parse_size.ccm +++ b/nihil.util/parse_size.ccm @@ -4,9 +4,9 @@ export module nihil.util:parse_size; import nihil.std; import nihil.core; import nihil.error; -import nihil.monad; import :ctype; +import :monad; namespace nihil { -- cgit v1.2.3