From e3f6805629f6f5ab752d33329ac0be4feb18d229 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 6 Dec 2018 12:47:14 +0900 Subject: [PATCH] add conversion members to result --- tests/test_result.cpp | 257 ++++++++++++++++++++++++++++++++++++++++++ toml/result.hpp | 155 +++++++++++++++++++++++++ 2 files changed, 412 insertions(+) diff --git a/tests/test_result.cpp b/tests/test_result.cpp index ea2249c..472f76b 100644 --- a/tests/test_result.cpp +++ b/tests/test_result.cpp @@ -109,3 +109,260 @@ BOOST_AUTO_TEST_CASE(test_assignment) BOOST_CHECK_EQUAL(result.unwrap_err(), "hoge"); } } + +BOOST_AUTO_TEST_CASE(test_map) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map( + [](const int i) -> int { + return i * 2; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42 * 2); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map( + [](std::unique_ptr i) -> int { + return *i; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map( + [](const int i) -> int { + return i * 2; + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).map( + [](std::unique_ptr i) -> int { + return *i; + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_map_err) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map_err( + [](const std::string s) -> std::string { + return s + s; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map_err( + [](const std::string s) -> std::string { + return s + s; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(*(mapped.unwrap()), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map_err( + [](const std::string s) -> std::string { + return s + s; + }); + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge"); + } + { + toml::result> + result(toml::err(std::unique_ptr(new std::string("hoge")))); + const auto mapped = std::move(result).map_err( + [](std::unique_ptr p) -> std::string { + return *p; + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_map_or_else) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map_or_else( + [](const int i) -> int { + return i * 2; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 42 * 2); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map_or_else( + [](std::unique_ptr i) -> int { + return *i; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map_or_else( + [](const int i) -> int { + return i * 2; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 54); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).map_or_else( + [](std::unique_ptr i) -> int { + return *i; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 54); + } +} + +BOOST_AUTO_TEST_CASE(test_and_then) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.and_then( + [](const int i) -> toml::result { + return toml::ok(i * 2); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42 * 2); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).and_then( + [](std::unique_ptr i) -> toml::result { + return toml::ok(*i); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.and_then( + [](const int i) -> toml::result { + return toml::ok(i * 2); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).and_then( + [](std::unique_ptr i) -> toml::result { + return toml::ok(*i); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_or_else) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.or_else( + [](const std::string& s) -> toml::result { + return toml::err(s + s); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).or_else( + [](const std::string& s) -> toml::result, std::string> { + return toml::err(s + s); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(*mapped.unwrap(), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.or_else( + [](const std::string& s) -> toml::result { + return toml::err(s + s); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).or_else( + [](const std::string& s) -> toml::result, std::string> { + return toml::err(s + s); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge"); + } +} diff --git a/toml/result.hpp b/toml/result.hpp index bdb4cae..67249f0 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -8,6 +8,19 @@ namespace toml { +#if __cplusplus >= 201703L + +template +using return_type_of_t = std::invoke_result_t; + +#else +// result_of is deprecated after C++17 +template +using return_type_of_t = typename std::result_of::type; + +#endif + + template struct success { @@ -390,6 +403,142 @@ struct result error_type const& as_err() const& noexcept {return this->fail.value;} error_type&& as_err() && noexcept {return std::move(this->fail.value);} + + // prerequisities + // F: T -> U + // retval: result + template + result, error_type> + map(F&& f) & + { + if(this->is_ok()){return ok(f(this->as_ok()));} + return err(this->as_err()); + } + template + result, error_type> + map(F&& f) const& + { + if(this->is_ok()){return ok(f(this->as_ok()));} + return err(this->as_err()); + } + template + result, error_type> + map(F&& f) && + { + if(this->is_ok()){return ok(f(std::move(this->as_ok())));} + return err(std::move(this->as_err())); + } + + // prerequisities + // F: E -> F + // retval: result + template + result> + map_err(F&& f) & + { + if(this->is_err()){return err(f(this->as_err()));} + return ok(this->as_ok()); + } + template + result> + map_err(F&& f) const& + { + if(this->is_err()){return err(f(this->as_err()));} + return ok(this->as_ok()); + } + template + result> + map_err(F&& f) && + { + if(this->is_err()){return err(f(std::move(this->as_err())));} + return ok(std::move(this->as_ok())); + } + + // prerequisities + // F: T -> U + // retval: U + template + return_type_of_t + map_or_else(F&& f, U&& opt) & + { + if(this->is_err()){return std::forward(opt);} + return f(this->as_ok()); + } + template + return_type_of_t + map_or_else(F&& f, U&& opt) const& + { + if(this->is_err()){return std::forward(opt);} + return f(this->as_ok()); + } + template + return_type_of_t + map_or_else(F&& f, U&& opt) && + { + if(this->is_err()){return std::forward(opt);} + return f(std::move(this->as_ok())); + } + + // prerequisities: + // F: func T -> U + // toml::err(error_type) should be convertible to U. + // normally, type U is another result and E is convertible to F + template + return_type_of_t + and_then(F&& f) & + { + if(this->is_ok()){return f(this->as_ok());} + return err(this->as_err()); + } + template + return_type_of_t + and_then(F&& f) const& + { + if(this->is_ok()){return f(this->as_ok());} + return err(this->as_err()); + } + template + return_type_of_t + and_then(F&& f) && + { + if(this->is_ok()){return f(std::move(this->as_ok()));} + return err(std::move(this->as_err())); + } + + // prerequisities: + // F: func E -> U + // toml::ok(value_type) should be convertible to U. + // normally, type U is another result and T is convertible to S + template + return_type_of_t + or_else(F&& f) & + { + if(this->is_err()){return f(this->as_err());} + return ok(this->as_ok()); + } + template + return_type_of_t + or_else(F&& f) const& + { + if(this->is_err()){return f(this->as_err());} + return ok(this->as_ok()); + } + template + return_type_of_t + or_else(F&& f) && + { + if(this->is_err()){return f(std::move(this->as_err()));} + return ok(std::move(this->as_ok())); + } + + void swap(result& other) + { + result tmp(std::move(*this)); + *this = std::move(other); + other = std::move(tmp); + return ; + } + private: void cleanup() noexcept @@ -409,6 +558,12 @@ struct result }; }; +template +void swap(result& lhs, result& rhs) +{ + lhs.swap(rhs); + return; +} } // toml11 #endif// TOML11_RESULT_H