| // { dg-do run { target c++23 } } |
| |
| #include <expected> |
| #include <string_view> |
| #include <testsuite_hooks.h> |
| |
| constexpr bool |
| test_and_then() |
| { |
| std::expected<int, int> e1(1); |
| VERIFY( e1.and_then([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, int&> ); |
| VERIFY( v == 1 ); |
| return std::expected<long, int>(100); |
| }).value() == 100 ); |
| VERIFY( std::move(e1).and_then([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, int> ); |
| VERIFY( v == 1 ); |
| return std::expected<long, int>(101); |
| }).value() == 101 ); |
| const auto& ce1 = e1; |
| VERIFY( ce1.and_then([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, const int&> ); |
| VERIFY( v == 1 ); |
| return std::expected<long, int>(102); |
| }).value() == 102 ); |
| VERIFY( std::move(ce1).and_then([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, const int> ); |
| VERIFY( v == 1 ); |
| return std::expected<long, int>(103); |
| }).value() == 103 ); |
| |
| auto fail = [] (auto&&) -> std::expected<void, int> { throw 1; }; |
| std::expected<int, int> e2(std::unexpect, 2); |
| VERIFY( e2.and_then(fail).error() == 2 ); |
| VERIFY( std::move(e2).and_then(fail).error() == 2 ); |
| const auto& ce2 = e2; |
| VERIFY( ce2.and_then(fail).error() == 2 ); |
| VERIFY( std::move(ce2).and_then(fail).error() == 2 ); |
| |
| int i = 100; |
| auto vpass = [&] -> std::expected<int, int> { return i++; }; |
| std::expected<void, int> v1; |
| VERIFY( v1.and_then(vpass).value() == 100 ); |
| VERIFY( std::move(v1).and_then(vpass).value() == 101 ); |
| const auto& cv1 = v1; |
| VERIFY( cv1.and_then(vpass).value() == 102 ); |
| VERIFY( std::move(cv1).and_then(vpass).value() == 103 ); |
| |
| auto vfail = [] -> std::expected<int, int> { throw 1; }; |
| std::expected<void, int> v2(std::unexpect, 2); |
| VERIFY( v2.and_then(vfail).error() == 2 ); |
| VERIFY( std::move(v2).and_then(vfail).error() == 2 ); |
| const auto& cv2 = v2; |
| VERIFY( cv2.and_then(vfail).error() == 2 ); |
| VERIFY( std::move(cv2).and_then(vfail).error() == 2 ); |
| |
| static_assert(std::is_same_v<decltype(v1.and_then(vpass)), |
| decltype(vpass())>); |
| static_assert(std::is_same_v<decltype(cv1.and_then(vpass)), |
| decltype(vpass())>); |
| |
| return true; |
| } |
| |
| constexpr bool |
| test_or_else() |
| { |
| std::expected<int, int> e1(std::unexpect, 1); |
| VERIFY( e1.or_else([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, int&> ); |
| VERIFY( v == 1 ); |
| return std::expected<int, long>(100); |
| }).value() == 100 ); |
| VERIFY( std::move(e1).or_else([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, int> ); |
| VERIFY( v == 1 ); |
| return std::expected<int, long>(101); |
| }).value() == 101 ); |
| const auto& ce1 = e1; |
| VERIFY( ce1.or_else([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, const int&> ); |
| VERIFY( v == 1 ); |
| return std::expected<int, long>(102); |
| }).value() == 102 ); |
| VERIFY( std::move(ce1).or_else([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, const int> ); |
| VERIFY( v == 1 ); |
| return std::expected<int, long>(103); |
| }).value() == 103 ); |
| |
| auto f = [] (auto) -> std::expected<int, long> { throw 1; }; |
| std::expected<int, int> e2(2); |
| VERIFY( e2.or_else(f).value() == 2 ); |
| VERIFY( std::move(e2).or_else(f).value() == 2 ); |
| const auto& ce2 = e2; |
| VERIFY( ce2.or_else(f).value() == 2 ); |
| VERIFY( std::move(ce2).or_else(f).value() == 2 ); |
| |
| auto vf = [] (auto) -> std::expected<void, long> { return {}; }; |
| std::expected<void, int> v1(std::unexpect, 1); |
| VERIFY( v1.or_else(vf).has_value() ); |
| VERIFY( std::move(v1).or_else(vf).has_value() ); |
| const auto& cv1 = v1; |
| VERIFY( cv1.or_else(vf).has_value() ); |
| VERIFY( std::move(cv1).or_else(vf).has_value() ); |
| |
| auto vfail = [] (auto) -> std::expected<void, long> { throw 1; }; |
| std::expected<void, int> v2; |
| VERIFY( v2.or_else(vfail).has_value() ); |
| VERIFY( std::move(v2).or_else(vfail).has_value() ); |
| const auto& cv2 = v2; |
| VERIFY( cv2.or_else(vfail).has_value() ); |
| VERIFY( std::move(cv2).or_else(vfail).has_value() ); |
| |
| static_assert(std::is_same_v<decltype(v1.or_else(vf)), decltype(vf(1))>); |
| static_assert(std::is_same_v<decltype(cv1.or_else(vf)), decltype(vf(1))>); |
| |
| return true; |
| } |
| |
| constexpr bool |
| test_transform() |
| { |
| std::expected<int, int> e1(1); |
| VERIFY( e1.transform([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, int&> ); |
| VERIFY( v == 1 ); |
| return std::string_view("100"); |
| }).value() == "100" ); |
| VERIFY( std::move(e1).transform([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, int> ); |
| VERIFY( v == 1 ); |
| return std::string_view("101"); |
| }).value() == "101" ); |
| const auto& ce1 = e1; |
| VERIFY( ce1.transform([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, const int&> ); |
| VERIFY( v == 1 ); |
| return std::string_view("102"); |
| }).value() == "102" ); |
| VERIFY( std::move(ce1).transform([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, const int> ); |
| VERIFY( v == 1 ); |
| return std::string_view("103"); |
| }).value() == "103" ); |
| |
| auto fail = [] (auto&&) -> std::string_view { throw 1; }; |
| std::expected<int, int> e2(std::unexpect, 2); |
| VERIFY( e2.transform(fail).error() == 2 ); |
| VERIFY( std::move(e2).transform(fail).error() == 2 ); |
| const auto& ce2 = e2; |
| VERIFY( ce2.transform(fail).error() == 2 ); |
| VERIFY( std::move(ce2).transform(fail).error() == 2 ); |
| |
| auto vpass = [&] -> std::string_view { return "ok"; }; |
| std::expected<void, int> v1; |
| VERIFY( v1.transform(vpass).value() == "ok" ); |
| VERIFY( std::move(v1).transform(vpass).value() == "ok" ); |
| const auto& cv1 = v1; |
| VERIFY( cv1.transform(vpass).value() == "ok" ); |
| VERIFY( std::move(cv1).transform(vpass).value() == "ok" ); |
| |
| auto vfail = [] -> std::string_view { throw 1; }; |
| std::expected<void, int> v2(std::unexpect, 2); |
| VERIFY( v2.transform(vfail).error() == 2 ); |
| VERIFY( std::move(v2).transform(vfail).error() == 2 ); |
| const auto& cv2 = v2; |
| VERIFY( cv2.transform(vfail).error() == 2 ); |
| VERIFY( std::move(cv2).transform(vfail).error() == 2 ); |
| |
| static_assert(std::is_same_v<decltype(v1.transform(vpass)), |
| std::expected<decltype(vpass()), int>>); |
| static_assert(std::is_same_v<decltype(cv1.transform(vpass)), |
| std::expected<decltype(vpass()), int>>); |
| |
| return true; |
| } |
| |
| constexpr bool |
| test_transform_error() |
| { |
| std::expected<int, int> e1(std::unexpect, 1); |
| VERIFY( e1.transform_error([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, int&> ); |
| VERIFY( v == 1 ); |
| return std::string_view("100"); |
| }).error() == "100" ); |
| VERIFY( std::move(e1).transform_error([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, int> ); |
| VERIFY( v == 1 ); |
| return std::string_view("101"); |
| }).error() == "101" ); |
| const auto& ce1 = e1; |
| VERIFY( ce1.transform_error([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, const int&> ); |
| VERIFY( v == 1 ); |
| return std::string_view("102"); |
| }).error() == "102" ); |
| VERIFY( std::move(ce1).transform_error([]<typename T>(T&& v) { |
| static_assert( std::is_same_v<T, const int> ); |
| VERIFY( v == 1 ); |
| return std::string_view("103"); |
| }).error() == "103" ); |
| |
| auto fail = [] (auto&&) -> std::string_view { throw 1; }; |
| std::expected<int, int> e2(2); |
| VERIFY( e2.transform_error(fail).value() == 2 ); |
| VERIFY( std::move(e2).transform_error(fail).value() == 2 ); |
| const auto& ce2 = e2; |
| VERIFY( ce2.transform_error(fail).value() == 2 ); |
| VERIFY( std::move(ce2).transform_error(fail).value() == 2 ); |
| |
| auto vpass = [&] (auto) -> std::string_view { return "ok"; }; |
| std::expected<void, int> v1(std::unexpect, 1); |
| VERIFY( v1.transform_error(vpass).error() == "ok" ); |
| VERIFY( std::move(v1).transform_error(vpass).error() == "ok" ); |
| const auto& cv1 = v1; |
| VERIFY( cv1.transform_error(vpass).error() == "ok" ); |
| VERIFY( std::move(cv1).transform_error(vpass).error() == "ok" ); |
| |
| auto vfail = [] (auto) -> std::string_view { throw 1; }; |
| std::expected<void, int> v2; |
| VERIFY( v2.transform_error(vfail).has_value() ); |
| VERIFY( std::move(v2).transform_error(vfail).has_value() ); |
| const auto& cv2 = v2; |
| VERIFY( cv2.transform_error(vfail).has_value() ); |
| VERIFY( std::move(cv2).transform_error(vfail).has_value() ); |
| |
| static_assert(std::is_same_v<decltype(v1.transform_error(vpass)), |
| std::expected<void, decltype(vpass(1))>>); |
| static_assert(std::is_same_v<decltype(cv1.transform_error(vpass)), |
| std::expected<void, decltype(vpass(1))>>); |
| |
| return true; |
| } |
| |
| constexpr bool |
| test_temporary_materialization() |
| { |
| struct NonCopyable { |
| constexpr NonCopyable(int i) : i(i) { } |
| NonCopyable(const NonCopyable&) = delete; |
| int i; |
| }; |
| |
| auto xform = [](int i) { return NonCopyable(i); }; |
| |
| std::expected<int, int> e1(1); |
| std::expected<NonCopyable, int> n1 = e1.transform(xform); |
| VERIFY( n1.value().i == 1 ); |
| std::expected<int, int> e2(std::unexpected<int>(2)); |
| std::expected<int, NonCopyable> n2 = e2.transform_error(xform); |
| VERIFY( n2.error().i == 2 ); |
| |
| auto vxform = [] { return NonCopyable(999); }; |
| std::expected<void, int> v1; |
| std::expected<NonCopyable, int> nv1 = v1.transform(vxform); |
| VERIFY( nv1.value().i == 999 ); |
| std::expected<void, int> v2(std::unexpected<int>(22)); |
| std::expected<void, NonCopyable> nv2 = v2.transform_error(xform); |
| VERIFY( nv2.error().i == 22 ); |
| |
| return true; |
| } |
| |
| int main() |
| { |
| static_assert( test_and_then() ); |
| test_and_then(); |
| static_assert( test_or_else() ); |
| test_or_else(); |
| static_assert( test_transform() ); |
| test_transform(); |
| static_assert( test_transform_error() ); |
| test_transform_error(); |
| static_assert( test_temporary_materialization() ); |
| test_temporary_materialization(); |
| } |