1 // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 // https://github.com/Dobiasd/FunctionalPlus 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 7 #include <doctest/doctest.h> 8 #include <fplus/fplus.hpp> 9 #include <vector> 10 11 namespace { 12 13 auto sqrtToMaybe = [](auto x) __anona315a4a30202(auto x) 14 { 15 return x < 0.0f ? fplus::nothing<float>() : 16 fplus::just(static_cast<float>(sqrt(static_cast<float>(x)))); 17 }; 18 19 auto sqrtToMaybeInt = [](auto x) __anona315a4a30302(auto x) 20 { 21 return x < 0 ? fplus::nothing<int>() : 22 fplus::just(fplus::round(sqrt(static_cast<float>(x)))); 23 }; 24 IntToFloat(const int & x)25 float IntToFloat(const int& x) 26 { 27 return static_cast<float>(x); 28 } 29 30 typedef std::vector<fplus::maybe<int>> IntMaybes; 31 typedef std::vector<int> Ints; 32 } 33 34 struct foo { foofoo35 explicit foo(int) { msgs_.push_back("ctor"); } foofoo36 foo(const foo&) { msgs_.push_back("copyctor"); } foofoo37 foo(foo&&) noexcept { msgs_.push_back("movector"); } operator =foo38 foo& operator = (const foo&) { msgs_.push_back("assignment"); return *this; } operator =foo39 foo& operator = (foo&&) { msgs_.push_back("moveassignment"); return *this; } ~foofoo40 ~foo() { msgs_.push_back("dtor"); } 41 static std::vector<std::string> msgs_; 42 }; 43 std::vector<std::string> foo::msgs_; 44 45 TEST_CASE("maybe_test - construction and assignment") 46 { 47 using namespace fplus; 48 foo::msgs_.clear(); 49 REQUIRE_EQ(maybe<int>(4), just<int>(4)); 50 51 typedef std::vector<std::string> Strings; 52 REQUIRE_EQ(foo::msgs_, Strings({})); 53 maybe<foo> no_foo; 54 REQUIRE_EQ(foo::msgs_, Strings({})); 55 { 56 foo foo_source = foo(1); 57 maybe<foo> a_foo(foo_source); 58 REQUIRE_EQ(foo::msgs_, Strings({"ctor", "copyctor"})); 59 } 60 REQUIRE_EQ(foo::msgs_, Strings({"ctor", "copyctor", "dtor", "dtor"})); 61 foo::msgs_.clear(); 62 } 63 64 TEST_CASE("maybe_test - move semantics") 65 { 66 using namespace fplus; 67 foo::msgs_.clear(); 68 typedef std::vector<std::string> Strings; 69 maybe<foo> foo_a(foo(1)); 70 maybe<foo> foo_b(foo(2)); 71 maybe<foo> foo_z(std::move(foo_a)); 72 foo_z = std::move(foo_b); 73 REQUIRE_EQ(foo::msgs_, Strings({ 74 "ctor", "movector", "dtor", 75 "ctor", "movector", "dtor", 76 "movector", "dtor", "movector"})); 77 foo::msgs_.clear(); 78 } 79 80 TEST_CASE("maybe_test - unsafe_get_just") 81 { 82 using namespace fplus; 83 fplus::maybe<int> m(4); 84 REQUIRE_EQ(m.unsafe_get_just(), 4); 85 m.unsafe_get_just() += 1; 86 REQUIRE_EQ(m.unsafe_get_just(), 5); 87 } 88 89 TEST_CASE("maybe_test - as_just_if") 90 { 91 using namespace fplus; 92 REQUIRE_EQ(as_just_if(is_even<int>, 4), just<int>(4)); 93 REQUIRE_EQ(as_just_if(is_even<int>, 5), nothing<int>()); 94 } 95 96 TEST_CASE("maybe_test - sequence") 97 { 98 using namespace fplus; 99 REQUIRE_EQ(maybe_to_seq(just(4)), std::vector<int>(1, 4)); 100 REQUIRE_EQ(maybe_to_seq(nothing<int>()), std::vector<int>()); 101 102 REQUIRE_EQ(singleton_seq_as_maybe(std::vector<int>()), nothing<int>()); 103 REQUIRE_EQ(singleton_seq_as_maybe(std::vector<int>(1, 4)), just(4)); 104 REQUIRE_EQ(singleton_seq_as_maybe(std::vector<int>(2, 4)), nothing<int>()); 105 } 106 107 TEST_CASE("maybe_test - just_with_default") 108 { 109 using namespace fplus; 110 auto x = just<int>(2); 111 maybe<int> y = nothing<int>(); 112 auto Or42 = bind_1st_of_2(just_with_default<int>, 42); 113 REQUIRE_EQ(Or42(x), 2); 114 REQUIRE_EQ(Or42(y), 42); 115 } 116 117 TEST_CASE("maybe_test - lift") 118 { 119 using namespace fplus; 120 auto x = just<int>(2); 121 maybe<int> y = nothing<int>(); __anona315a4a30402(auto n) 122 auto squareGeneric = [](auto n) { return n * n; }; 123 124 REQUIRE_EQ(lift_maybe(square<int>, x), just(4)); 125 REQUIRE_EQ(lift_maybe(square<int>, y), nothing<int>()); 126 REQUIRE_EQ(lift_maybe(squareGeneric, x), just(4)); 127 REQUIRE_EQ(lift_maybe(squareGeneric, y), nothing<int>()); 128 auto SquareAndSquare = compose(square<int>, square<int>); 129 REQUIRE_EQ(lift_maybe(SquareAndSquare, x), just(16)); 130 131 REQUIRE_EQ(lift_maybe_def(3, square<int>, x), 4); 132 REQUIRE_EQ(lift_maybe_def(3, square<int>, y), 3); 133 REQUIRE_EQ(lift_maybe_def(3, squareGeneric, x), 4); 134 REQUIRE_EQ(lift_maybe_def(3, squareGeneric, y), 3); 135 136 REQUIRE_EQ(lift_maybe_2(std::plus<int>(), x, x), just(4)); 137 REQUIRE_EQ(lift_maybe_2(std::plus<int>(), x, y), y); 138 REQUIRE_EQ(lift_maybe_2(std::plus<>(), y, x), y); 139 REQUIRE_EQ(lift_maybe_2(std::plus<>(), y, y), y); 140 141 REQUIRE_EQ(lift_maybe_2_def(3, std::plus<int>(), x, x), 4); 142 REQUIRE_EQ(lift_maybe_2_def(3, std::plus<int>(), x, y), 3); 143 REQUIRE_EQ(lift_maybe_2_def(3, std::plus<>(), y, x), 3); 144 REQUIRE_EQ(lift_maybe_2_def(3, std::plus<>(), y, y), 3); 145 } 146 147 TEST_CASE("maybe_test - join_maybe") 148 { 149 using namespace fplus; 150 REQUIRE_EQ(join_maybe(just(just(2))), just(2)); 151 REQUIRE_EQ(join_maybe(just(nothing<int>())), nothing<int>()); 152 REQUIRE_EQ(join_maybe(nothing<maybe<int>>()), nothing<int>()); 153 } 154 155 TEST_CASE("maybe_test - and_then_maybe") 156 { 157 using namespace fplus; 158 REQUIRE_EQ(and_then_maybe(sqrtToMaybeInt, just(4)), just(2)); 159 REQUIRE_EQ(and_then_maybe(sqrtToMaybeInt, nothing<int>()), nothing<int>()); 160 const auto string_to_maybe_int = [](const auto& str) __anona315a4a30502(const auto& str) 161 { 162 if (str == "42") return just<int>(42); 163 else return nothing<int>(); 164 }; 165 REQUIRE_EQ(and_then_maybe(string_to_maybe_int, just<std::string>("3")), nothing<int>()); 166 REQUIRE_EQ(and_then_maybe(string_to_maybe_int, just<std::string>("42")), just<int>(42)); 167 REQUIRE_EQ(and_then_maybe(string_to_maybe_int, nothing<std::string>()), nothing<int>()); 168 } 169 170 TEST_CASE("maybe_test - compose") 171 { 172 using namespace fplus; 173 auto sqrtAndSqrt = compose_maybe(sqrtToMaybe, sqrtToMaybe); 174 auto sqrtIntAndSqrtIntAndSqrtInt = compose_maybe(sqrtToMaybeInt, sqrtToMaybeInt, sqrtToMaybeInt); 175 REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtInt(256), just(2)); 176 auto sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt = compose_maybe(sqrtToMaybeInt, sqrtToMaybeInt, sqrtToMaybeInt, sqrtToMaybeInt); 177 REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt(65536), just(2)); 178 179 auto LiftedIntToFloat = [](const maybe<int>& m) -> maybe<float> __anona315a4a30602(const maybe<int>& m) 180 { 181 return lift_maybe(IntToFloat, m); 182 }; 183 auto JustInt = just<int>; 184 auto IntToMaybeFloat = compose(JustInt, LiftedIntToFloat); 185 auto IntToFloatAndSqrtAndSqrt = compose_maybe(IntToMaybeFloat, sqrtAndSqrt); 186 __anona315a4a30702(auto n) 187 auto squareMaybe = [](auto n) { return just<decltype(n * n)>(n * n); }; __anona315a4a30802(auto m, auto n) 188 auto plusMaybe = [](auto m, auto n) { 189 return just<decltype(m + n)>(m + n); 190 }; 191 REQUIRE_EQ(compose_maybe(plusMaybe, squareMaybe)(2, 3), just(25)); 192 REQUIRE(is_in_interval(1.41f, 1.42f, unsafe_get_just<float> 193 (IntToFloatAndSqrtAndSqrt(4)))); 194 } 195 196 TEST_CASE("maybe_test - equality") 197 { 198 using namespace fplus; 199 IntMaybes maybes = {just(1), nothing<int>(), just(2)}; 200 REQUIRE(justs(maybes) == Ints({ 1,2 })); 201 REQUIRE(just(1) == just(1)); 202 REQUIRE(just(1) != just(2)); 203 REQUIRE(just(1) != nothing<int>()); 204 REQUIRE(nothing<int>() == nothing<int>()); 205 } 206 207 TEST_CASE("maybe_test - transform_and_keep_justs") 208 { 209 using namespace fplus; 210 Ints wholeNumbers = { -3, 4, 16, -1 }; 211 REQUIRE_EQ(transform_and_keep_justs(sqrtToMaybeInt, wholeNumbers) 212 , Ints({2,4})); 213 REQUIRE_EQ(transform_and_concat( 214 bind_1st_of_2(replicate<int>, std::size_t(3)), Ints{ 1,2 }) 215 , Ints({ 1,1,1,2,2,2 })); 216 } 217 218 TEST_CASE("maybe_test - show_maybe") 219 { 220 using namespace fplus; 221 REQUIRE_EQ(show_maybe(just<int>(42)), std::string("Just 42")); 222 REQUIRE_EQ(show_maybe(nothing<int>()), std::string("Nothing")); 223 } 224 225 TEST_CASE("maybe_test - exceptions") 226 { 227 using namespace fplus; 228 std::string thrown_str; 229 try 230 { 231 throw_on_nothing(std::invalid_argument("raised"), nothing<int>()); 232 } 233 catch (const std::exception& e) 234 { 235 thrown_str = e.what(); 236 } 237 REQUIRE_EQ(thrown_str, std::string("raised")); 238 } 239 240 TEST_CASE("maybe_test - copy") 241 { 242 using namespace fplus; 243 maybe<int> maybe_4(4); 244 maybe<int> maybe_4_copy(maybe_4); 245 maybe<int> maybe_4_copy_2; 246 maybe_4_copy_2 = maybe_4_copy; 247 REQUIRE_EQ(maybe_4_copy_2, just<int>(4)); 248 } 249 250 TEST_CASE("maybe_test - flatten") 251 { 252 using namespace fplus; 253 maybe<int> maybe_int_nothing; 254 maybe<int> maybe_int_nothing_copy(maybe_int_nothing); 255 maybe<int> maybe_int_nothing_copy_2; 256 maybe_int_nothing_copy_2 = maybe_int_nothing_copy; 257 REQUIRE_EQ(maybe_int_nothing_copy_2, nothing<int>()); 258 REQUIRE_EQ(flatten_maybe(maybe<maybe<int>>(maybe<int>(1))), maybe<int>(1)); 259 REQUIRE_EQ(flatten_maybe(maybe<maybe<int>>(maybe<int>())), nothing<int>()); 260 REQUIRE_EQ(flatten_maybe(maybe<maybe<int>>()), nothing<int>()); 261 } 262