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.
This commit is contained in:
parent
59822c3920
commit
8d9e2776a6
7 changed files with 882 additions and 4 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -65,3 +65,4 @@ test_expected_error()
|
|||
}
|
||||
|
||||
// { dg-prune-output "static assertion failed" }
|
||||
// { dg-prune-output "function returning an array" }
|
||||
|
|
280
libstdc++-v3/testsuite/20_util/expected/monadic.cc
Normal file
280
libstdc++-v3/testsuite/20_util/expected/monadic.cc
Normal file
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue