From 52c783731e02b79dce1b6b9f71fd1ceb27141708 Mon Sep 17 00:00:00 2001 From: Nerijus Arlauskas Date: Thu, 7 Jul 2016 01:10:45 +0300 Subject: [PATCH] Implement and_then and use simpler magic. --- src/maybe/result.hpp | 7 +++++- src/maybe/result.inline.hpp | 16 +++++++++++-- tests/CMakeLists.txt | 4 +++- tests/result_and_then_tests.cpp | 41 +++++++++++++++++++++++++++++++++ tests/result_chaining_tests.cpp | 28 ---------------------- tests/result_map_tests.cpp | 40 ++++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 32 deletions(-) create mode 100644 tests/result_and_then_tests.cpp delete mode 100644 tests/result_chaining_tests.cpp create mode 100644 tests/result_map_tests.cpp diff --git a/src/maybe/result.hpp b/src/maybe/result.hpp index bfa8d57..b2e5e56 100644 --- a/src/maybe/result.hpp +++ b/src/maybe/result.hpp @@ -50,6 +50,8 @@ namespace maybe { template class result final { public: + typedef T ok_type; + result() : tag(internal::Value::NONE) { } @@ -194,7 +196,10 @@ namespace maybe { } template - constexpr auto map(F f) noexcept -> maybe::result())), E>; + constexpr auto map(F f) noexcept -> maybe::result::type, E>; + + template + constexpr auto and_then(F f) noexcept -> typename std::result_of::type; private: constexpr void copy_from(const result& other) noexcept; diff --git a/src/maybe/result.inline.hpp b/src/maybe/result.inline.hpp index b00cf5c..c7885f6 100644 --- a/src/maybe/result.inline.hpp +++ b/src/maybe/result.inline.hpp @@ -99,12 +99,24 @@ constexpr void maybe::result::clear() noexcept template template constexpr auto maybe::result::map(F f) noexcept - -> maybe::result())), E> + -> maybe::result::type, E> { - typedef maybe::result())), E> return_result_t; + typedef maybe::result::type, E> return_result_t; if (is_err()) { return return_result_t::err(std::forward(err_value())); } return return_result_t::ok(f(std::forward(ok_value()))); +}; + +template +template +constexpr auto maybe::result::and_then(F f) noexcept -> typename std::result_of::type +{ + typedef typename std::result_of::type result_t; + + if (is_err()) { + return maybe::result::err(std::forward(err_value())); + } + return f(std::forward(ok_value())); }; \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1da5cfe..df38a68 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,7 +5,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") add_executable(${TARGET} main.cpp result_tests.cpp - result_chaining_tests.cpp) + result_map_tests.cpp + result_and_then_tests.cpp + ) target_include_directories(${TARGET} PUBLIC $ diff --git a/tests/result_and_then_tests.cpp b/tests/result_and_then_tests.cpp new file mode 100644 index 0000000..f0ec392 --- /dev/null +++ b/tests/result_and_then_tests.cpp @@ -0,0 +1,41 @@ +#include "catch.hpp" + +#include + +class A final { +public: + A(std::string value) : value(value) + { + } + std::string value; +}; + +class B final { +public: + B(std::string value) : value(value) + { + } + std::string value; +}; + +using maybe::result; + +TEST_CASE("result_and_then") +{ + SECTION("chains another function returning different result if previous one was successful") + { + auto a = result::ok(A("hello")); + auto b = a.and_then([](A v) { return result::ok(B(v.value + " world")); }); + REQUIRE(b); + REQUIRE(b.ok_value().value == "hello world"); + } + + SECTION( + "should not run another function returning different result if previous one returned error") + { + auto a = result::err(43); + auto b = a.and_then([](A v) { return result::ok(B(v.value + " world")); }); + REQUIRE(!b); + REQUIRE(b.err_value() == 43); + } +} \ No newline at end of file diff --git a/tests/result_chaining_tests.cpp b/tests/result_chaining_tests.cpp deleted file mode 100644 index 399361f..0000000 --- a/tests/result_chaining_tests.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "catch.hpp" - -#include - -class A final { -public: - A(std::string value) : value(value) {} - std::string value; -}; - -class B final { -public: - B(std::string value) : value(value) {} - std::string value; -}; - -using maybe::result; - -TEST_CASE("result_chaining") -{ - SECTION("converts result A to result B") - { - auto a = result::ok(A("hello")); - auto b = a.map([](A v) { return B(v.value); }); - REQUIRE(b); - REQUIRE(b.ok_value().value == "hello"); - } -} \ No newline at end of file diff --git a/tests/result_map_tests.cpp b/tests/result_map_tests.cpp new file mode 100644 index 0000000..1d97f62 --- /dev/null +++ b/tests/result_map_tests.cpp @@ -0,0 +1,40 @@ +#include "catch.hpp" + +#include + +class A final { +public: + A(std::string value) : value(value) + { + } + std::string value; +}; + +class B final { +public: + B(std::string value) : value(value) + { + } + std::string value; +}; + +using maybe::result; + +TEST_CASE("result_map") +{ + SECTION("converts result A to result B") + { + auto a = result::ok(A("hello")); + auto b = a.map([](A v) { return B(v.value + " world"); }); + REQUIRE(b); + REQUIRE(b.ok_value().value == "hello world"); + } + + SECTION("does not convert result A to result B if value was error") + { + auto a = result::err(43); + auto b = a.map([](A v) { return B(v.value = " world"); }); + REQUIRE(!b); + REQUIRE(b.err_value() == 43); + } +} \ No newline at end of file