Brian Silverman | da86135 | 2019-02-02 16:42:28 -0800 | [diff] [blame] | 1 | #include "catch.hpp" |
| 2 | #include "optional.hpp" |
| 3 | #include <string> |
| 4 | |
| 5 | #define TOKENPASTE(x, y) x##y |
| 6 | #define TOKENPASTE2(x, y) TOKENPASTE(x, y) |
| 7 | #define STATIC_REQUIRE(e) \ |
| 8 | constexpr bool TOKENPASTE2(rqure, __LINE__) = e; \ |
| 9 | REQUIRE(e); |
| 10 | |
| 11 | constexpr int get_int(int) { return 42; } |
| 12 | TL_OPTIONAL_11_CONSTEXPR tl::optional<int> get_opt_int(int) { return 42; } |
| 13 | |
| 14 | // What is Clang Format up to?! |
| 15 | TEST_CASE("Monadic operations", "[monadic]") { |
| 16 | SECTION("map") { // lhs is empty |
| 17 | tl::optional<int> o1; |
| 18 | auto o1r = o1.map([](int i) { return i + 2; }); |
| 19 | STATIC_REQUIRE((std::is_same<decltype(o1r), tl::optional<int>>::value)); |
| 20 | REQUIRE(!o1r); |
| 21 | |
| 22 | // lhs has value |
| 23 | tl::optional<int> o2 = 40; |
| 24 | auto o2r = o2.map([](int i) { return i + 2; }); |
| 25 | STATIC_REQUIRE((std::is_same<decltype(o2r), tl::optional<int>>::value)); |
| 26 | REQUIRE(o2r.value() == 42); |
| 27 | |
| 28 | struct rval_call_map { |
| 29 | double operator()(int) && { return 42.0; }; |
| 30 | }; |
| 31 | |
| 32 | // ensure that function object is forwarded |
| 33 | tl::optional<int> o3 = 42; |
| 34 | auto o3r = o3.map(rval_call_map{}); |
| 35 | STATIC_REQUIRE((std::is_same<decltype(o3r), tl::optional<double>>::value)); |
| 36 | REQUIRE(o3r.value() == 42); |
| 37 | |
| 38 | // ensure that lhs is forwarded |
| 39 | tl::optional<int> o4 = 40; |
| 40 | auto o4r = std::move(o4).map([](int &&i) { return i + 2; }); |
| 41 | STATIC_REQUIRE((std::is_same<decltype(o4r), tl::optional<int>>::value)); |
| 42 | REQUIRE(o4r.value() == 42); |
| 43 | |
| 44 | // ensure that lhs is const-propagated |
| 45 | const tl::optional<int> o5 = 40; |
| 46 | auto o5r = o5.map([](const int &i) { return i + 2; }); |
| 47 | STATIC_REQUIRE((std::is_same<decltype(o5r), tl::optional<int>>::value)); |
| 48 | REQUIRE(o5r.value() == 42); |
| 49 | |
| 50 | // test void return |
| 51 | tl::optional<int> o7 = 40; |
| 52 | auto f7 = [](const int &i) { return; }; |
| 53 | auto o7r = o7.map(f7); |
| 54 | STATIC_REQUIRE( |
| 55 | (std::is_same<decltype(o7r), tl::optional<tl::monostate>>::value)); |
| 56 | REQUIRE(o7r.has_value()); |
| 57 | |
| 58 | // test each overload in turn |
| 59 | tl::optional<int> o8 = 42; |
| 60 | auto o8r = o8.map([](int) { return 42; }); |
| 61 | REQUIRE(*o8r == 42); |
| 62 | |
| 63 | tl::optional<int> o9 = 42; |
| 64 | auto o9r = o9.map([](int) { return; }); |
| 65 | REQUIRE(o9r); |
| 66 | |
| 67 | tl::optional<int> o12 = 42; |
| 68 | auto o12r = std::move(o12).map([](int) { return 42; }); |
| 69 | REQUIRE(*o12r == 42); |
| 70 | |
| 71 | tl::optional<int> o13 = 42; |
| 72 | auto o13r = std::move(o13).map([](int) { return; }); |
| 73 | REQUIRE(o13r); |
| 74 | |
| 75 | const tl::optional<int> o16 = 42; |
| 76 | auto o16r = o16.map([](int) { return 42; }); |
| 77 | REQUIRE(*o16r == 42); |
| 78 | |
| 79 | const tl::optional<int> o17 = 42; |
| 80 | auto o17r = o17.map([](int) { return; }); |
| 81 | REQUIRE(o17r); |
| 82 | |
| 83 | const tl::optional<int> o20 = 42; |
| 84 | auto o20r = std::move(o20).map([](int) { return 42; }); |
| 85 | REQUIRE(*o20r == 42); |
| 86 | |
| 87 | const tl::optional<int> o21 = 42; |
| 88 | auto o21r = std::move(o21).map([](int) { return; }); |
| 89 | REQUIRE(o21r); |
| 90 | |
| 91 | tl::optional<int> o24 = tl::nullopt; |
| 92 | auto o24r = o24.map([](int) { return 42; }); |
| 93 | REQUIRE(!o24r); |
| 94 | |
| 95 | tl::optional<int> o25 = tl::nullopt; |
| 96 | auto o25r = o25.map([](int) { return; }); |
| 97 | REQUIRE(!o25r); |
| 98 | |
| 99 | tl::optional<int> o28 = tl::nullopt; |
| 100 | auto o28r = std::move(o28).map([](int) { return 42; }); |
| 101 | REQUIRE(!o28r); |
| 102 | |
| 103 | tl::optional<int> o29 = tl::nullopt; |
| 104 | auto o29r = std::move(o29).map([](int) { return; }); |
| 105 | REQUIRE(!o29r); |
| 106 | |
| 107 | const tl::optional<int> o32 = tl::nullopt; |
| 108 | auto o32r = o32.map([](int) { return 42; }); |
| 109 | REQUIRE(!o32r); |
| 110 | |
| 111 | const tl::optional<int> o33 = tl::nullopt; |
| 112 | auto o33r = o33.map([](int) { return; }); |
| 113 | REQUIRE(!o33r); |
| 114 | |
| 115 | const tl::optional<int> o36 = tl::nullopt; |
| 116 | auto o36r = std::move(o36).map([](int) { return 42; }); |
| 117 | REQUIRE(!o36r); |
| 118 | |
| 119 | const tl::optional<int> o37 = tl::nullopt; |
| 120 | auto o37r = std::move(o37).map([](int) { return; }); |
| 121 | REQUIRE(!o37r); |
| 122 | |
| 123 | // callable which returns a reference |
| 124 | tl::optional<int> o38 = 42; |
| 125 | auto o38r = o38.map([](int &i) -> const int & { return i; }); |
| 126 | REQUIRE(o38r); |
| 127 | REQUIRE(*o38r == 42); |
| 128 | |
| 129 | int i = 42; |
| 130 | tl::optional<int&> o39 = i; |
| 131 | o39.map([](int& x){x = 12;}); |
| 132 | REQUIRE(i == 12); |
| 133 | } |
| 134 | |
| 135 | SECTION("map constexpr") { |
| 136 | #if !defined(_MSC_VER) && defined(TL_OPTIONAL_CXX14) |
| 137 | // test each overload in turn |
| 138 | constexpr tl::optional<int> o16 = 42; |
| 139 | constexpr auto o16r = o16.map(get_int); |
| 140 | STATIC_REQUIRE(*o16r == 42); |
| 141 | |
| 142 | constexpr tl::optional<int> o20 = 42; |
| 143 | constexpr auto o20r = std::move(o20).map(get_int); |
| 144 | STATIC_REQUIRE(*o20r == 42); |
| 145 | |
| 146 | constexpr tl::optional<int> o32 = tl::nullopt; |
| 147 | constexpr auto o32r = o32.map(get_int); |
| 148 | STATIC_REQUIRE(!o32r); |
| 149 | constexpr tl::optional<int> o36 = tl::nullopt; |
| 150 | constexpr auto o36r = std::move(o36).map(get_int); |
| 151 | STATIC_REQUIRE(!o36r); |
| 152 | #endif |
| 153 | } |
| 154 | |
| 155 | SECTION("and_then") { |
| 156 | |
| 157 | // lhs is empty |
| 158 | tl::optional<int> o1; |
| 159 | auto o1r = o1.and_then([](int i) { return tl::optional<float>{42}; }); |
| 160 | STATIC_REQUIRE((std::is_same<decltype(o1r), tl::optional<float>>::value)); |
| 161 | REQUIRE(!o1r); |
| 162 | |
| 163 | // lhs has value |
| 164 | tl::optional<int> o2 = 12; |
| 165 | auto o2r = o2.and_then([](int i) { return tl::optional<float>{42}; }); |
| 166 | STATIC_REQUIRE((std::is_same<decltype(o2r), tl::optional<float>>::value)); |
| 167 | REQUIRE(o2r.value() == 42.f); |
| 168 | |
| 169 | // lhs is empty, rhs returns empty |
| 170 | tl::optional<int> o3; |
| 171 | auto o3r = o3.and_then([](int i) { return tl::optional<float>{}; }); |
| 172 | STATIC_REQUIRE((std::is_same<decltype(o3r), tl::optional<float>>::value)); |
| 173 | REQUIRE(!o3r); |
| 174 | |
| 175 | // rhs returns empty |
| 176 | tl::optional<int> o4 = 12; |
| 177 | auto o4r = o4.and_then([](int i) { return tl::optional<float>{}; }); |
| 178 | STATIC_REQUIRE((std::is_same<decltype(o4r), tl::optional<float>>::value)); |
| 179 | REQUIRE(!o4r); |
| 180 | |
| 181 | struct rval_call_and_then { |
| 182 | tl::optional<double> operator()(int) && { |
| 183 | return tl::optional<double>(42.0); |
| 184 | }; |
| 185 | }; |
| 186 | |
| 187 | // ensure that function object is forwarded |
| 188 | tl::optional<int> o5 = 42; |
| 189 | auto o5r = o5.and_then(rval_call_and_then{}); |
| 190 | STATIC_REQUIRE((std::is_same<decltype(o5r), tl::optional<double>>::value)); |
| 191 | REQUIRE(o5r.value() == 42); |
| 192 | |
| 193 | // ensure that lhs is forwarded |
| 194 | tl::optional<int> o6 = 42; |
| 195 | auto o6r = |
| 196 | std::move(o6).and_then([](int &&i) { return tl::optional<double>(i); }); |
| 197 | STATIC_REQUIRE((std::is_same<decltype(o6r), tl::optional<double>>::value)); |
| 198 | REQUIRE(o6r.value() == 42); |
| 199 | |
| 200 | // ensure that function object is const-propagated |
| 201 | const tl::optional<int> o7 = 42; |
| 202 | auto o7r = |
| 203 | o7.and_then([](const int &i) { return tl::optional<double>(i); }); |
| 204 | STATIC_REQUIRE((std::is_same<decltype(o7r), tl::optional<double>>::value)); |
| 205 | REQUIRE(o7r.value() == 42); |
| 206 | |
| 207 | // test each overload in turn |
| 208 | tl::optional<int> o8 = 42; |
| 209 | auto o8r = o8.and_then([](int i) { return tl::make_optional(42); }); |
| 210 | REQUIRE(*o8r == 42); |
| 211 | |
| 212 | tl::optional<int> o9 = 42; |
| 213 | auto o9r = |
| 214 | std::move(o9).and_then([](int i) { return tl::make_optional(42); }); |
| 215 | REQUIRE(*o9r == 42); |
| 216 | |
| 217 | const tl::optional<int> o10 = 42; |
| 218 | auto o10r = o10.and_then([](int i) { return tl::make_optional(42); }); |
| 219 | REQUIRE(*o10r == 42); |
| 220 | |
| 221 | const tl::optional<int> o11 = 42; |
| 222 | auto o11r = |
| 223 | std::move(o11).and_then([](int i) { return tl::make_optional(42); }); |
| 224 | REQUIRE(*o11r == 42); |
| 225 | |
| 226 | tl::optional<int> o16 = tl::nullopt; |
| 227 | auto o16r = o16.and_then([](int i) { return tl::make_optional(42); }); |
| 228 | REQUIRE(!o16r); |
| 229 | |
| 230 | tl::optional<int> o17 = tl::nullopt; |
| 231 | auto o17r = |
| 232 | std::move(o17).and_then([](int i) { return tl::make_optional(42); }); |
| 233 | REQUIRE(!o17r); |
| 234 | |
| 235 | const tl::optional<int> o18 = tl::nullopt; |
| 236 | auto o18r = o18.and_then([](int i) { return tl::make_optional(42); }); |
| 237 | REQUIRE(!o18r); |
| 238 | |
| 239 | const tl::optional<int> o19 = tl::nullopt; |
| 240 | auto o19r = std::move(o19).and_then([](int i) { return tl::make_optional(42); }); |
| 241 | REQUIRE(!o19r); |
| 242 | |
| 243 | int i = 3; |
| 244 | tl::optional<int&> o20{i}; |
| 245 | std::move(o20).and_then([](int& r){return tl::optional<int&>{++r};}); |
| 246 | REQUIRE(o20); |
| 247 | REQUIRE(i == 4); |
| 248 | } |
| 249 | |
| 250 | SECTION("constexpr and_then") { |
| 251 | #if !defined(_MSC_VER) && defined(TL_OPTIONAL_CXX14) |
| 252 | |
| 253 | constexpr tl::optional<int> o10 = 42; |
| 254 | constexpr auto o10r = o10.and_then(get_opt_int); |
| 255 | REQUIRE(*o10r == 42); |
| 256 | |
| 257 | constexpr tl::optional<int> o11 = 42; |
| 258 | constexpr auto o11r = std::move(o11).and_then(get_opt_int); |
| 259 | REQUIRE(*o11r == 42); |
| 260 | |
| 261 | constexpr tl::optional<int> o18 = tl::nullopt; |
| 262 | constexpr auto o18r = o18.and_then(get_opt_int); |
| 263 | REQUIRE(!o18r); |
| 264 | |
| 265 | constexpr tl::optional<int> o19 = tl::nullopt; |
| 266 | constexpr auto o19r = std::move(o19).and_then(get_opt_int); |
| 267 | REQUIRE(!o19r); |
| 268 | #endif |
| 269 | } |
| 270 | |
| 271 | SECTION("or else") { |
| 272 | tl::optional<int> o1 = 42; |
| 273 | REQUIRE(*(o1.or_else([] { return tl::make_optional(13); })) == 42); |
| 274 | |
| 275 | tl::optional<int> o2; |
| 276 | REQUIRE(*(o2.or_else([] { return tl::make_optional(13); })) == 13); |
| 277 | } |
| 278 | |
| 279 | SECTION("disjunction") { |
| 280 | tl::optional<int> o1 = 42; |
| 281 | tl::optional<int> o2 = 12; |
| 282 | tl::optional<int> o3; |
| 283 | |
| 284 | REQUIRE(*o1.disjunction(o2) == 42); |
| 285 | REQUIRE(*o1.disjunction(o3) == 42); |
| 286 | REQUIRE(*o2.disjunction(o1) == 12); |
| 287 | REQUIRE(*o2.disjunction(o3) == 12); |
| 288 | REQUIRE(*o3.disjunction(o1) == 42); |
| 289 | REQUIRE(*o3.disjunction(o2) == 12); |
| 290 | } |
| 291 | |
| 292 | SECTION("conjunction") { |
| 293 | tl::optional<int> o1 = 42; |
| 294 | REQUIRE(*o1.conjunction(42.0) == 42.0); |
| 295 | REQUIRE(*o1.conjunction(std::string{"hello"}) == std::string{"hello"}); |
| 296 | |
| 297 | tl::optional<int> o2; |
| 298 | REQUIRE(!o2.conjunction(42.0)); |
| 299 | REQUIRE(!o2.conjunction(std::string{"hello"})); |
| 300 | } |
| 301 | |
| 302 | SECTION("map_or") { |
| 303 | tl::optional<int> o1 = 21; |
| 304 | REQUIRE((o1.map_or([](int x) { return x * 2; }, 13)) == 42); |
| 305 | |
| 306 | tl::optional<int> o2; |
| 307 | REQUIRE((o2.map_or([](int x) { return x * 2; }, 13)) == 13); |
| 308 | } |
| 309 | |
| 310 | SECTION("map_or_else") { |
| 311 | tl::optional<int> o1 = 21; |
| 312 | REQUIRE((o1.map_or_else([](int x) { return x * 2; }, [] { return 13; })) == |
| 313 | 42); |
| 314 | |
| 315 | tl::optional<int> o2; |
| 316 | REQUIRE((o2.map_or_else([](int x) { return x * 2; }, [] { return 13; })) == |
| 317 | 13); |
| 318 | } |
| 319 | |
| 320 | SECTION("take") { |
| 321 | tl::optional<int> o1 = 42; |
| 322 | REQUIRE(*o1.take() == 42); |
| 323 | REQUIRE(!o1); |
| 324 | |
| 325 | tl::optional<int> o2; |
| 326 | REQUIRE(!o2.take()); |
| 327 | REQUIRE(!o2); |
| 328 | } |
| 329 | |
| 330 | struct foo { |
| 331 | void non_const() {} |
| 332 | }; |
| 333 | |
| 334 | #if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ |
| 335 | !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) |
| 336 | SECTION("Issue #1") { |
| 337 | tl::optional<foo> f = foo{}; |
| 338 | auto l = [](auto &&x) { x.non_const(); }; |
| 339 | f.map(l); |
| 340 | } |
| 341 | #endif |
| 342 | |
| 343 | struct overloaded { |
| 344 | tl::optional<int> operator()(foo &) { return 0; } |
| 345 | tl::optional<std::string> operator()(const foo &) { return ""; } |
| 346 | }; |
| 347 | |
| 348 | SECTION("Issue #2") { |
| 349 | tl::optional<foo> f = foo{}; |
| 350 | auto x = f.and_then(overloaded{}); |
| 351 | } |
| 352 | }; |