diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2022-12-15 15:47:38 +0000 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2022-12-16 20:59:19 +0000 |
commit | 8d9e2776a6d2bfe6662f79182e85ab79f3cc9522 (patch) | |
tree | 7b22eb46864c3f12e0b90766c27b22ce8155d424 /libstdc++-v3 | |
parent | 59822c39207c9e8be576e9d6c3370bd85ddaf886 (diff) | |
download | gcc-8d9e2776a6d2bfe6662f79182e85ab79f3cc9522.zip gcc-8d9e2776a6d2bfe6662f79182e85ab79f3cc9522.tar.gz gcc-8d9e2776a6d2bfe6662f79182e85ab79f3cc9522.tar.bz2 |
libstdc++: Add monadic operations to std::expected for C++23 (P2505R5)
This was approved for C++23 last month in Kona.
libstdc++-v3/ChangeLog:
* include/std/expected (expected): Add monadic operations.
(expected<void, E>): Likewise.
* include/std/version (__cpp_lib_expected): Bump value.
* testsuite/20_util/expected/synopsis.cc: Adjust expected macro
value.
* testsuite/20_util/expected/version.cc: Likewise.
* testsuite/20_util/expected/illformed_neg.cc: Prune additional
errors from ill-formed monadic operations.
* testsuite/20_util/expected/observers.cc: Check error_or.
* testsuite/20_util/expected/monadic.cc: New test.
Diffstat (limited to 'libstdc++-v3')
-rw-r--r-- | libstdc++-v3/include/std/expected | 579 | ||||
-rw-r--r-- | libstdc++-v3/include/std/version | 2 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc | 1 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/expected/monadic.cc | 280 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/expected/observers.cc | 20 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/expected/synopsis.cc | 2 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/expected/version.cc | 2 |
7 files changed, 882 insertions, 4 deletions
diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected index 2fe25a9..5557795 100644 --- a/libstdc++-v3/include/std/expected +++ b/libstdc++-v3/include/std/expected @@ -35,6 +35,7 @@ #include <initializer_list> #include <bits/exception.h> // exception +#include <bits/invoke.h> // __invoke #include <bits/stl_construct.h> // construct_at #include <bits/utility.h> // in_place_t @@ -49,7 +50,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @{ */ -#define __cpp_lib_expected 202202L +#define __cpp_lib_expected 202211L /// Discriminated union that holds an expected value or an error value. /** @@ -151,11 +152,20 @@ namespace __expected template<typename _Tp> constexpr bool __is_unexpected<unexpected<_Tp>> = true; + template<typename _Fn, typename _Tp> + using __result = remove_cvref_t<invoke_result_t<_Fn&&, _Tp&&>>; + template<typename _Fn> + using __result0 = remove_cvref_t<invoke_result_t<_Fn&&>>; + template<typename _Er> concept __can_be_unexpected = is_object_v<_Er> && (!is_array_v<_Er>) && (!__expected::__is_unexpected<_Er>) && (!is_const_v<_Er>) && (!is_volatile_v<_Er>); + + // Tag types for in-place construction from an invocation result. + struct __in_place_inv { }; + struct __unexpect_inv { }; } /// @endcond @@ -334,6 +344,14 @@ namespace __expected __not_<is_convertible<_Err, _Er>> >; + template<typename _Up> + static constexpr bool __same_val + = is_same_v<typename _Up::value_type, _Tp>; + + template<typename _Up> + static constexpr bool __same_err + = is_same_v<typename _Up::error_type, _Er>; + public: using value_type = _Tp; using error_type = _Er; @@ -791,6 +809,274 @@ namespace __expected return static_cast<_Tp>(std::forward<_Up>(__v)); } + template<typename _Gr = _Er> + constexpr _Er + error_or(_Gr&& __e) const& + { + static_assert( is_copy_constructible_v<_Er> ); + static_assert( is_convertible_v<_Gr, _Er> ); + + if (_M_has_value) + return std::forward<_Gr>(__e); + return _M_unex; + } + + template<typename _Gr = _Er> + constexpr _Er + error_or(_Gr&& __e) && + { + static_assert( is_move_constructible_v<_Er> ); + static_assert( is_convertible_v<_Gr, _Er> ); + + if (_M_has_value) + return std::forward<_Gr>(__e); + return std::move(_M_unex); + } + + // monadic operations + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + and_then(_Fn&& __f) & + { + using _Up = __expected::__result<_Fn, _Tp&>; + static_assert(__expected::__is_expected<_Up>); + static_assert(is_same_v<typename _Up::error_type, _Er>); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), value()); + else + return _Up(unexpect, error()); + } + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + and_then(_Fn&& __f) const & + { + using _Up = __expected::__result<_Fn, const _Tp&>; + static_assert(__expected::__is_expected<_Up>); + static_assert(is_same_v<typename _Up::error_type, _Er>); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), value()); + else + return _Up(unexpect, error()); + } + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + and_then(_Fn&& __f) && + { + using _Up = __expected::__result<_Fn, _Tp&&>; + static_assert(__expected::__is_expected<_Up>); + static_assert(is_same_v<typename _Up::error_type, _Er>); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), std::move(value())); + else + return _Up(unexpect, std::move(error())); + } + + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + and_then(_Fn&& __f) const && + { + using _Up = __expected::__result<_Fn, const _Tp&&>; + static_assert(__expected::__is_expected<_Up>); + static_assert(is_same_v<typename _Up::error_type, _Er>); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), std::move(value())); + else + return _Up(unexpect, std::move(error())); + } + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + or_else(_Fn&& __f) & + { + using _Gr = __expected::__result<_Fn, _Er&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v<typename _Gr::value_type, _Tp>); + + if (has_value()) + return _Gr(in_place, value()); + else + return std::__invoke(std::forward<_Fn>(__f), error()); + } + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + or_else(_Fn&& __f) const & + { + using _Gr = __expected::__result<_Fn, const _Er&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v<typename _Gr::value_type, _Tp>); + + if (has_value()) + return _Gr(in_place, value()); + else + return std::__invoke(std::forward<_Fn>(__f), error()); + } + + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + or_else(_Fn&& __f) && + { + using _Gr = __expected::__result<_Fn, _Er&&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v<typename _Gr::value_type, _Tp>); + + if (has_value()) + return _Gr(in_place, std::move(value())); + else + return std::__invoke(std::forward<_Fn>(__f), std::move(error())); + } + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + or_else(_Fn&& __f) const && + { + using _Gr = __expected::__result<_Fn, const _Er&&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v<typename _Gr::value_type, _Tp>); + + if (has_value()) + return _Gr(in_place, std::move(value())); + else + return std::__invoke(std::forward<_Fn>(__f), std::move(error())); + } + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + transform(_Fn&& __f) & + { + using _Up = __expected::__result<_Fn, _Tp&>; + using _Res = expected<_Up, _Er>; + + if (has_value()) + return _Res(__in_place_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + _M_val); + }); + else + return _Res(unexpect, std::move(error())); + } + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + transform(_Fn&& __f) const & + { + using _Up = __expected::__result<_Fn, const _Tp&>; + using _Res = expected<_Up, _Er>; + + if (has_value()) + return _Res(__in_place_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + _M_val); + }); + else + return _Res(unexpect, std::move(error())); + } + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + transform(_Fn&& __f) && + { + using _Up = __expected::__result<_Fn, _Tp>; + using _Res = expected<_Up, _Er>; + + if (has_value()) + return _Res(__in_place_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + std::move(_M_val)); + }); + else + return _Res(unexpect, std::move(error())); + } + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + transform(_Fn&& __f) const && + { + using _Up = __expected::__result<_Fn, const _Tp>; + using _Res = expected<_Up, _Er>; + + if (has_value()) + return _Res(__in_place_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + std::move(_M_val)); + }); + else + return _Res(unexpect, std::move(error())); + } + + template<typename _Fn> requires is_copy_constructible_v<_Tp> + constexpr auto + transform_error(_Fn&& __f) & + { + using _Gr = __expected::__result<_Fn, _Er&>; + using _Res = expected<_Tp, _Gr>; + + if (has_value()) + return _Res(in_place, value()); + else + return _Res(__unexpect_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + _M_unex); + }); + } + + template<typename _Fn> requires is_copy_constructible_v<_Tp> + constexpr auto + transform_error(_Fn&& __f) const & + { + using _Gr = __expected::__result<_Fn, const _Er&>; + using _Res = expected<_Tp, _Gr>; + + if (has_value()) + return _Res(in_place, value()); + else + return _Res(__unexpect_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + _M_unex); + }); + } + + template<typename _Fn> requires is_move_constructible_v<_Tp> + constexpr auto + transform_error(_Fn&& __f) && + { + using _Gr = __expected::__result<_Fn, _Er&&>; + using _Res = expected<_Tp, _Gr>; + + if (has_value()) + return _Res(in_place, std::move(value())); + else + return _Res(__unexpect_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + std::move(_M_unex)); + }); + } + + template<typename _Fn> requires is_move_constructible_v<_Tp> + constexpr auto + transform_error(_Fn&& __f) const && + { + using _Gr = __expected::__result<_Fn, const _Er&&>; + using _Res = expected<_Tp, _Gr>; + + if (has_value()) + return _Res(in_place, std::move(value())); + else + return _Res(__unexpect_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + std::move(_M_unex)); + }); + } + // equality operators template<typename _Up, typename _Er2> @@ -888,6 +1174,21 @@ namespace __expected } } + using __in_place_inv = __expected::__in_place_inv; + using __unexpect_inv = __expected::__unexpect_inv; + + template<typename _Fn> + explicit constexpr + expected(__in_place_inv, _Fn&& __fn) + : _M_val(std::forward<_Fn>(__fn)()), _M_has_value(true) + { } + + template<typename _Fn> + explicit constexpr + expected(__unexpect_inv, _Fn&& __fn) + : _M_unex(std::forward<_Fn>(__fn)()), _M_has_value(false) + { } + union { _Tp _M_val; _Er _M_unex; @@ -910,6 +1211,14 @@ namespace __expected is_constructible<_Unex, const expected<_Up, _Err>> >; + template<typename _Up> + static constexpr bool __same_val + = is_same_v<typename _Up::value_type, _Tp>; + + template<typename _Up> + static constexpr bool __same_err + = is_same_v<typename _Up::error_type, _Er>; + public: using value_type = _Tp; using error_type = _Er; @@ -1180,6 +1489,260 @@ namespace __expected return std::move(_M_unex); } + template<typename _Gr = _Er> + constexpr _Er + error_or(_Gr&& __e) const& + { + static_assert( is_copy_constructible_v<_Er> ); + static_assert( is_convertible_v<_Gr, _Er> ); + + if (_M_has_value) + return std::forward<_Gr>(__e); + return _M_unex; + } + + template<typename _Gr = _Er> + constexpr _Er + error_or(_Gr&& __e) && + { + static_assert( is_move_constructible_v<_Er> ); + static_assert( is_convertible_v<_Gr, _Er> ); + + if (_M_has_value) + return std::forward<_Gr>(__e); + return std::move(_M_unex); + } + + // monadic operations + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + and_then(_Fn&& __f) & + { + using _Up = __expected::__result0<_Fn>; + static_assert(__expected::__is_expected<_Up>); + static_assert(is_same_v<typename _Up::error_type, _Er>); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f)); + else + return _Up(unexpect, error()); + } + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + and_then(_Fn&& __f) const & + { + using _Up = __expected::__result0<_Fn>; + static_assert(__expected::__is_expected<_Up>); + static_assert(is_same_v<typename _Up::error_type, _Er>); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f)); + else + return _Up(unexpect, error()); + } + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + and_then(_Fn&& __f) && + { + using _Up = __expected::__result0<_Fn>; + static_assert(__expected::__is_expected<_Up>); + static_assert(is_same_v<typename _Up::error_type, _Er>); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f)); + else + return _Up(unexpect, std::move(error())); + } + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + and_then(_Fn&& __f) const && + { + using _Up = __expected::__result0<_Fn>; + static_assert(__expected::__is_expected<_Up>); + static_assert(is_same_v<typename _Up::error_type, _Er>); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f)); + else + return _Up(unexpect, std::move(error())); + } + + template<typename _Fn> + constexpr auto + or_else(_Fn&& __f) & + { + using _Gr = __expected::__result<_Fn, _Er&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v<typename _Gr::value_type, _Tp>); + + if (has_value()) + return _Gr(); + else + return std::__invoke(std::forward<_Fn>(__f), error()); + } + + template<typename _Fn> + constexpr auto + or_else(_Fn&& __f) const & + { + using _Gr = __expected::__result<_Fn, const _Er&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v<typename _Gr::value_type, _Tp>); + + if (has_value()) + return _Gr(); + else + return std::__invoke(std::forward<_Fn>(__f), error()); + } + + template<typename _Fn> + constexpr auto + or_else(_Fn&& __f) && + { + using _Gr = __expected::__result<_Fn, _Er&&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v<typename _Gr::value_type, _Tp>); + + if (has_value()) + return _Gr(); + else + return std::__invoke(std::forward<_Fn>(__f), std::move(error())); + } + + template<typename _Fn> + constexpr auto + or_else(_Fn&& __f) const && + { + using _Gr = __expected::__result<_Fn, const _Er&&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v<typename _Gr::value_type, _Tp>); + + if (has_value()) + return _Gr(); + else + return std::__invoke(std::forward<_Fn>(__f), std::move(error())); + } + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + transform(_Fn&& __f) & + { + using _Up = __expected::__result0<_Fn>; + using _Res = expected<_Up, _Er>; + + if (has_value()) + return _Res(__in_place_inv{}, std::forward<_Fn>(__f)); + else + return _Res(unexpect, error()); + } + + template<typename _Fn> requires is_copy_constructible_v<_Er> + constexpr auto + transform(_Fn&& __f) const & + { + using _Up = __expected::__result0<_Fn>; + using _Res = expected<_Up, _Er>; + + if (has_value()) + return _Res(__in_place_inv{}, std::forward<_Fn>(__f)); + else + return _Res(unexpect, error()); + } + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + transform(_Fn&& __f) && + { + using _Up = __expected::__result0<_Fn>; + using _Res = expected<_Up, _Er>; + + if (has_value()) + return _Res(__in_place_inv{}, std::forward<_Fn>(__f)); + else + return _Res(unexpect, std::move(error())); + } + + template<typename _Fn> requires is_move_constructible_v<_Er> + constexpr auto + transform(_Fn&& __f) const && + { + using _Up = __expected::__result0<_Fn>; + using _Res = expected<_Up, _Er>; + + if (has_value()) + return _Res(__in_place_inv{}, std::forward<_Fn>(__f)); + else + return _Res(unexpect, std::move(error())); + } + + template<typename _Fn> + constexpr auto + transform_error(_Fn&& __f) & + { + using _Gr = __expected::__result<_Fn, _Er&>; + using _Res = expected<_Tp, _Gr>; + + if (has_value()) + return _Res(); + else + return _Res(__unexpect_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + _M_unex); + }); + } + + template<typename _Fn> + constexpr auto + transform_error(_Fn&& __f) const & + { + using _Gr = __expected::__result<_Fn, const _Er&>; + using _Res = expected<_Tp, _Gr>; + + if (has_value()) + return _Res(); + else + return _Res(__unexpect_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + _M_unex); + }); + } + + template<typename _Fn> + constexpr auto + transform_error(_Fn&& __f) && + { + using _Gr = __expected::__result<_Fn, _Er&&>; + using _Res = expected<_Tp, _Gr>; + + if (has_value()) + return _Res(); + else + return _Res(__unexpect_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + std::move(_M_unex)); + }); + } + + template<typename _Fn> + constexpr auto + transform_error(_Fn&& __f) const && + { + using _Gr = __expected::__result<_Fn, const _Er&&>; + using _Res = expected<_Tp, _Gr>; + + if (has_value()) + return _Res(); + else + return _Res(__unexpect_inv{}, [&]() { + return std::__invoke(std::forward<_Fn>(__f), + std::move(_M_unex)); + }); + } + // equality operators template<typename _Up, typename _Er2> @@ -1223,6 +1786,20 @@ namespace __expected _M_unex = std::forward<_Vp>(__v); } + using __in_place_inv = __expected::__in_place_inv; + using __unexpect_inv = __expected::__unexpect_inv; + + template<typename _Fn> + explicit constexpr + expected(__in_place_inv, _Fn&& __fn) + : _M_void(), _M_has_value(true) + { std::forward<_Fn>(__fn)(); } + + template<typename _Fn> + explicit constexpr + expected(__unexpect_inv, _Fn&& __fn) + : _M_unex(std::forward<_Fn>(__fn)()), _M_has_value(false) + { } union { struct { } _M_void; diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 61718eb..576eebc 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -306,7 +306,7 @@ #define __cpp_lib_constexpr_charconv 202207L #define __cpp_lib_constexpr_typeinfo 202106L #if __cpp_concepts >= 202002L -# define __cpp_lib_expected 202202L +# define __cpp_lib_expected 202211L #endif #define __cpp_lib_invoke_r 202106L #define __cpp_lib_is_scoped_enum 202011L diff --git a/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc b/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc index 921306bc..f1b0771 100644 --- a/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc +++ b/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc @@ -65,3 +65,4 @@ test_expected_error() } // { dg-prune-output "static assertion failed" } +// { dg-prune-output "function returning an array" } diff --git a/libstdc++-v3/testsuite/20_util/expected/monadic.cc b/libstdc++-v3/testsuite/20_util/expected/monadic.cc new file mode 100644 index 0000000..d82774b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/expected/monadic.cc @@ -0,0 +1,280 @@ +// { dg-options "-std=gnu++23" } +// { 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(); +} diff --git a/libstdc++-v3/testsuite/20_util/expected/observers.cc b/libstdc++-v3/testsuite/20_util/expected/observers.cc index e76ca37..9bcd9ed 100644 --- a/libstdc++-v3/testsuite/20_util/expected/observers.cc +++ b/libstdc++-v3/testsuite/20_util/expected/observers.cc @@ -191,6 +191,24 @@ test_value_or() return true; } +constexpr bool +test_error_or() +{ + std::expected<int, int> e1(1), e2(std::unexpect, 3); + VERIFY( e1.error_or(2) == 2 ); + VERIFY( std::move(e1).error_or(2) == 2 ); + VERIFY( e2.error_or(2) == 3 ); + VERIFY( std::move(e2).error_or(2) == 3 ); + + std::expected<void, int> e3, e4(std::unexpect, 3); + VERIFY( e3.error_or(2) == 2 ); + VERIFY( std::move(e3).error_or(2) == 2 ); + VERIFY( e4.error_or(2) == 3 ); + VERIFY( std::move(e4).error_or(2) == 3 ); + + return true; +} + int main() { static_assert( test_arrow() ); @@ -206,4 +224,6 @@ int main() test_error(); static_assert( test_value_or() ); test_value_or(); + static_assert( test_error_or() ); + test_error_or(); } diff --git a/libstdc++-v3/testsuite/20_util/expected/synopsis.cc b/libstdc++-v3/testsuite/20_util/expected/synopsis.cc index 3a7eef3..b043961 100644 --- a/libstdc++-v3/testsuite/20_util/expected/synopsis.cc +++ b/libstdc++-v3/testsuite/20_util/expected/synopsis.cc @@ -6,7 +6,7 @@ #ifndef __cpp_lib_expected # error "Feature-test macro for expected missing in <expected>" -#elif __cpp_lib_expected != 202202L +#elif __cpp_lib_expected != 202211L # error "Feature-test macro for expected has wrong value in <expected>" #endif diff --git a/libstdc++-v3/testsuite/20_util/expected/version.cc b/libstdc++-v3/testsuite/20_util/expected/version.cc index 78dc61e..98fccf7 100644 --- a/libstdc++-v3/testsuite/20_util/expected/version.cc +++ b/libstdc++-v3/testsuite/20_util/expected/version.cc @@ -5,6 +5,6 @@ #ifndef __cpp_lib_expected # error "Feature-test macro for expected missing in <version>" -#elif __cpp_lib_expected != 202202L +#elif __cpp_lib_expected != 202211L # error "Feature-test macro for expected has wrong value in <version>" #endif |