From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id 78BCD384EF4D; Fri, 16 Dec 2022 21:00:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 78BCD384EF4D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1671224421; bh=L9arm5iWtjUMmAvkv1s/IDx4Db2I9hSvQDw3NGjbsxc=; h=From:To:Subject:Date:From; b=ySs84f8Y+rONtLC7PLBgMUnJn1YAD2b9qba1ojd0d4pAWxTRgga7sKmCilaD4u39F Tv4knk/18XWEidHtqgAGyV9mLwH0v46GdRI7s6hTPfwczfdMILJqjJsTu382FitjGR DnUWGOVv7bDqf9krFrgwmAx0WqWMaEklIXQXrrMI= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r13-4754] libstdc++: Add monadic operations to std::expected for C++23 (P2505R5) X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/master X-Git-Oldrev: 59822c39207c9e8be576e9d6c3370bd85ddaf886 X-Git-Newrev: 8d9e2776a6d2bfe6662f79182e85ab79f3cc9522 Message-Id: <20221216210021.78BCD384EF4D@sourceware.org> Date: Fri, 16 Dec 2022 21:00:21 +0000 (GMT) List-Id: https://gcc.gnu.org/g:8d9e2776a6d2bfe6662f79182e85ab79f3cc9522 commit r13-4754-g8d9e2776a6d2bfe6662f79182e85ab79f3cc9522 Author: Jonathan Wakely Date: Thu Dec 15 15:47:38 2022 +0000 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): 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. Diff: --- libstdc++-v3/include/std/expected | 579 ++++++++++++++++++++- libstdc++-v3/include/std/version | 2 +- .../testsuite/20_util/expected/illformed_neg.cc | 1 + libstdc++-v3/testsuite/20_util/expected/monadic.cc | 280 ++++++++++ .../testsuite/20_util/expected/observers.cc | 20 + .../testsuite/20_util/expected/synopsis.cc | 2 +- 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 2fe25a90d2d..555779581f2 100644 --- a/libstdc++-v3/include/std/expected +++ b/libstdc++-v3/include/std/expected @@ -35,6 +35,7 @@ #include #include // exception +#include // __invoke #include // construct_at #include // 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 constexpr bool __is_unexpected> = true; + template + using __result = remove_cvref_t>; + template + using __result0 = remove_cvref_t>; + template 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_> >; + template + static constexpr bool __same_val + = is_same_v; + + template + static constexpr bool __same_err + = is_same_v; + public: using value_type = _Tp; using error_type = _Er; @@ -791,6 +809,274 @@ namespace __expected return static_cast<_Tp>(std::forward<_Up>(__v)); } + template + 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 + 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 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); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), value()); + else + return _Up(unexpect, error()); + } + + template 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); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), value()); + else + return _Up(unexpect, error()); + } + + template 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); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), std::move(value())); + else + return _Up(unexpect, std::move(error())); + } + + + template 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); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), std::move(value())); + else + return _Up(unexpect, std::move(error())); + } + + template 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); + + if (has_value()) + return _Gr(in_place, value()); + else + return std::__invoke(std::forward<_Fn>(__f), error()); + } + + template 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); + + if (has_value()) + return _Gr(in_place, value()); + else + return std::__invoke(std::forward<_Fn>(__f), error()); + } + + + template 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); + + if (has_value()) + return _Gr(in_place, std::move(value())); + else + return std::__invoke(std::forward<_Fn>(__f), std::move(error())); + } + + template 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); + + if (has_value()) + return _Gr(in_place, std::move(value())); + else + return std::__invoke(std::forward<_Fn>(__f), std::move(error())); + } + + template 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 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 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 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 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 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 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 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 @@ -888,6 +1174,21 @@ namespace __expected } } + using __in_place_inv = __expected::__in_place_inv; + using __unexpect_inv = __expected::__unexpect_inv; + + template + explicit constexpr + expected(__in_place_inv, _Fn&& __fn) + : _M_val(std::forward<_Fn>(__fn)()), _M_has_value(true) + { } + + template + 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 + static constexpr bool __same_val + = is_same_v; + + template + static constexpr bool __same_err + = is_same_v; + public: using value_type = _Tp; using error_type = _Er; @@ -1180,6 +1489,260 @@ namespace __expected return std::move(_M_unex); } + template + 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 + 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 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); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f)); + else + return _Up(unexpect, error()); + } + + template 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); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f)); + else + return _Up(unexpect, error()); + } + + template 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); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f)); + else + return _Up(unexpect, std::move(error())); + } + + template 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); + + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f)); + else + return _Up(unexpect, std::move(error())); + } + + template + constexpr auto + or_else(_Fn&& __f) & + { + using _Gr = __expected::__result<_Fn, _Er&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v); + + if (has_value()) + return _Gr(); + else + return std::__invoke(std::forward<_Fn>(__f), error()); + } + + template + 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); + + if (has_value()) + return _Gr(); + else + return std::__invoke(std::forward<_Fn>(__f), error()); + } + + template + constexpr auto + or_else(_Fn&& __f) && + { + using _Gr = __expected::__result<_Fn, _Er&&>; + static_assert(__expected::__is_expected<_Gr>); + static_assert(is_same_v); + + if (has_value()) + return _Gr(); + else + return std::__invoke(std::forward<_Fn>(__f), std::move(error())); + } + + template + 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); + + if (has_value()) + return _Gr(); + else + return std::__invoke(std::forward<_Fn>(__f), std::move(error())); + } + + template 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 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 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 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 + 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 + 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 + 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 + 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 @@ -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 + explicit constexpr + expected(__in_place_inv, _Fn&& __fn) + : _M_void(), _M_has_value(true) + { std::forward<_Fn>(__fn)(); } + + template + 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 61718ebad74..576eebc7dc8 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 921306bc667..f1b0771aeb9 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 00000000000..d82774b0e1f --- /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 +#include +#include + +constexpr bool +test_and_then() +{ + std::expected e1(1); + VERIFY( e1.and_then([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::expected(100); + }).value() == 100 ); + VERIFY( std::move(e1).and_then([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::expected(101); + }).value() == 101 ); + const auto& ce1 = e1; + VERIFY( ce1.and_then([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::expected(102); + }).value() == 102 ); + VERIFY( std::move(ce1).and_then([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::expected(103); + }).value() == 103 ); + + auto fail = [] (auto&&) -> std::expected { throw 1; }; + std::expected 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 { return i++; }; + std::expected 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 { throw 1; }; + std::expected 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); + static_assert(std::is_same_v); + + return true; +} + +constexpr bool +test_or_else() +{ + std::expected e1(std::unexpect, 1); + VERIFY( e1.or_else([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::expected(100); + }).value() == 100 ); + VERIFY( std::move(e1).or_else([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::expected(101); + }).value() == 101 ); + const auto& ce1 = e1; + VERIFY( ce1.or_else([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::expected(102); + }).value() == 102 ); + VERIFY( std::move(ce1).or_else([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::expected(103); + }).value() == 103 ); + + auto f = [] (auto) -> std::expected { throw 1; }; + std::expected 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 { return {}; }; + std::expected 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 { throw 1; }; + std::expected 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); + static_assert(std::is_same_v); + + return true; +} + +constexpr bool +test_transform() +{ + std::expected e1(1); + VERIFY( e1.transform([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::string_view("100"); + }).value() == "100" ); + VERIFY( std::move(e1).transform([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::string_view("101"); + }).value() == "101" ); + const auto& ce1 = e1; + VERIFY( ce1.transform([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::string_view("102"); + }).value() == "102" ); + VERIFY( std::move(ce1).transform([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::string_view("103"); + }).value() == "103" ); + + auto fail = [] (auto&&) -> std::string_view { throw 1; }; + std::expected 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 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 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>); + static_assert(std::is_same_v>); + + return true; +} + +constexpr bool +test_transform_error() +{ + std::expected e1(std::unexpect, 1); + VERIFY( e1.transform_error([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::string_view("100"); + }).error() == "100" ); + VERIFY( std::move(e1).transform_error([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::string_view("101"); + }).error() == "101" ); + const auto& ce1 = e1; + VERIFY( ce1.transform_error([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::string_view("102"); + }).error() == "102" ); + VERIFY( std::move(ce1).transform_error([](T&& v) { + static_assert( std::is_same_v ); + VERIFY( v == 1 ); + return std::string_view("103"); + }).error() == "103" ); + + auto fail = [] (auto&&) -> std::string_view { throw 1; }; + std::expected 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 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 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>); + static_assert(std::is_same_v>); + + 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 e1(1); + std::expected n1 = e1.transform(xform); + VERIFY( n1.value().i == 1 ); + std::expected e2(std::unexpected(2)); + std::expected n2 = e2.transform_error(xform); + VERIFY( n2.error().i == 2 ); + + auto vxform = [] { return NonCopyable(999); }; + std::expected v1; + std::expected nv1 = v1.transform(vxform); + VERIFY( nv1.value().i == 999 ); + std::expected v2(std::unexpected(22)); + std::expected 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 e76ca378026..9bcd9edb809 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 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 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 3a7eef3eee4..b0439614a27 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 " -#elif __cpp_lib_expected != 202202L +#elif __cpp_lib_expected != 202211L # error "Feature-test macro for expected has wrong value in " #endif diff --git a/libstdc++-v3/testsuite/20_util/expected/version.cc b/libstdc++-v3/testsuite/20_util/expected/version.cc index 78dc61ef55d..98fccf73dc1 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 " -#elif __cpp_lib_expected != 202202L +#elif __cpp_lib_expected != 202211L # error "Feature-test macro for expected has wrong value in " #endif