Support void for map_error

This commit is contained in:
Simon Brand
2017-12-03 08:25:19 +00:00
parent e081092fb1
commit 9423013aca
2 changed files with 97 additions and 14 deletions

View File

@@ -921,9 +921,9 @@ public:
/// \group and_then /// \group and_then
/// Carries out some operation which returns an expected on the stored object /// Carries out some operation which returns an expected on the stored object
/// if there is one. \requires `std::invoke(std::forward<F>(f), value())` /// if there is one. \requires `std::invoke(std::forward<F>(f), value())`
/// returns a `std::expected<U>` for some `U`. \returns Let `U` be the result /// returns an `expected<U>` for some `U`. \returns Let `U` be the result
/// of `std::invoke(std::forward<F>(f), value())`. Returns a /// of `std::invoke(std::forward<F>(f), value())`. Returns an
/// `std::expected<U>`. The return value is empty if `*this` is empty, /// `expected<U>`. The return value is empty if `*this` is empty,
/// otherwise the return value of `std::invoke(std::forward<F>(f), value())` /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
/// is returned. /// is returned.
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &;
@@ -975,9 +975,9 @@ public:
/// \group and_then /// \group and_then
/// Carries out some operation which returns an expected on the stored object /// Carries out some operation which returns an expected on the stored object
/// if there is one. \requires `std::invoke(std::forward<F>(f), value())` /// if there is one. \requires `std::invoke(std::forward<F>(f), value())`
/// returns a `std::expected<U>` for some `U`. \returns Let `U` be the result /// returns an `expected<U>` for some `U`. \returns Let `U` be the result
/// of `std::invoke(std::forward<F>(f), value())`. Returns a /// of `std::invoke(std::forward<F>(f), value())`. Returns an
/// `std::expected<U>`. The return value is empty if `*this` is empty, /// `expected<U>`. The return value is empty if `*this` is empty,
/// otherwise the return value of `std::invoke(std::forward<F>(f), value())` /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
/// is returned. /// is returned.
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &;
@@ -1034,7 +1034,8 @@ public:
!defined(TL_EXPECTED_GCC54) !defined(TL_EXPECTED_GCC54)
/// \brief Carries out some operation on the stored object if there is one. /// \brief Carries out some operation on the stored object if there is one.
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f), /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
/// value())`. Returns a `std::expected<U,E>`. If `*this` is unexpected, the /// value())`. If `U` is `void`, returns an `expected<monostate,E>, otherwise
// returns an `expected<U,E>`. If `*this` is unexpected, the
/// result is `*this`, otherwise an `expected<U,E>` is constructed from the /// result is `*this`, otherwise an `expected<U,E>` is constructed from the
/// return value of `std::invoke(std::forward<F>(f), value())` and is /// return value of `std::invoke(std::forward<F>(f), value())` and is
/// returned. /// returned.
@@ -1065,7 +1066,8 @@ public:
#else #else
/// \brief Carries out some operation on the stored object if there is one. /// \brief Carries out some operation on the stored object if there is one.
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f), /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
/// value())`. Returns a `std::expected<U,E>`. If `*this` is unexpected, the /// value())`. If `U` is `void`, returns an `expected<monostate,E>, otherwise
// returns an `expected<U,E>`. If `*this` is unexpected, the
/// result is `*this`, otherwise an `expected<U,E>` is constructed from the /// result is `*this`, otherwise an `expected<U,E>` is constructed from the
/// return value of `std::invoke(std::forward<F>(f), value())` and is /// return value of `std::invoke(std::forward<F>(f), value())` and is
/// returned. /// returned.
@@ -1114,7 +1116,8 @@ public:
/// \brief Carries out some operation on the stored unexpected object if there /// \brief Carries out some operation on the stored unexpected object if there
/// is one. /// is one.
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f), /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
/// value())`. Returns a `std::expected<T,U>`. If `*this` has an expected /// value())`. If `U` is `void`, returns an `expected<T,monostate>`, otherwise
/// returns an `expected<T,U>`. If `*this` has an expected
/// value, the result is `*this`, otherwise an `expected<T,U>` is constructed /// value, the result is `*this`, otherwise an `expected<T,U>` is constructed
/// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is /// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is
/// returned. /// returned.
@@ -1146,7 +1149,7 @@ public:
/// \brief Carries out some operation on the stored unexpected object if there /// \brief Carries out some operation on the stored unexpected object if there
/// is one. /// is one.
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f), /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
/// value())`. Returns a `std::expected<T,U>`. If `*this` has an expected /// value())`. Returns an `expected<T,U>`. If `*this` has an expected
/// value, the result is `*this`, otherwise an `expected<T,U>` is constructed /// value, the result is `*this`, otherwise an `expected<T,U>` is constructed
/// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is /// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is
/// returned. /// returned.
@@ -1622,6 +1625,7 @@ public:
/// \exclude /// \exclude
namespace detail { namespace detail {
template <class Exp> using exp_t = typename detail::decay_t<Exp>::error_type;
template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type; template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type;
template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>; template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>;
@@ -1683,19 +1687,35 @@ auto map_impl(Exp &&exp, F &&f) -> expected<monostate, err_t<Exp>> {
!defined(TL_EXPECTED_GCC54) !defined(TL_EXPECTED_GCC54)
template <class Exp, class F, template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(), class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>()))> *std::declval<Exp>())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
constexpr auto map_error_impl(Exp &&exp, F &&f) { constexpr auto map_error_impl(Exp &&exp, F &&f) {
using result = ret_t<Exp, Ret>; using result = expected<exp_t<Exp>, Ret>;
return exp.has_value() return exp.has_value()
? result(*std::forward<Exp>(exp)) ? result(*std::forward<Exp>(exp))
: result(unexpect, detail::invoke(std::forward<F>(f), : result(unexpect, detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error())); std::forward<Exp>(exp).error()));
} }
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>())),
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
constexpr auto map_error_impl(Exp &&exp, F &&f) {
using result = expected<exp_t<Exp>, monostate>;
if (exp.has_value()) {
return result(*std::forward<Exp>(exp));
}
detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error());
return result(unexpect, monostate{});
}
#else #else
template <class Exp, class F, template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(), class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>()))> *std::declval<Exp>())),
constexpr auto map_error_impl(Exp &&exp, F &&f) -> ret_t<Exp, Ret> { detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
constexpr auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, Ret> {
using result = ret_t<Exp, Ret>; using result = ret_t<Exp, Ret>;
return exp.has_value() return exp.has_value()
@@ -1703,6 +1723,21 @@ constexpr auto map_error_impl(Exp &&exp, F &&f) -> ret_t<Exp, Ret> {
: result(unexpect, detail::invoke(std::forward<F>(f), : result(unexpect, detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error())); std::forward<Exp>(exp).error()));
} }
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>())),
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
constexpr auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
using result = expected<exp_t<Exp>, monostate>;
if (exp.has_value()) {
return result(*std::forward<Exp>(exp));
}
detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error());
return result(unexpect, monostate{});
}
#endif #endif
template <class Exp, class F, template <class Exp, class F,

View File

@@ -191,6 +191,54 @@ TEST_CASE("Map error extensions", "[extensions.map_error]") {
REQUIRE(!ret); REQUIRE(!ret);
REQUIRE(ret.error() == 42); REQUIRE(ret.error() == 42);
} }
{
tl::expected<int, int> e = 21;
auto ret = e.map_error(ret_void);
REQUIRE(ret);
}
{
const tl::expected<int, int> e = 21;
auto ret = e.map_error(ret_void);
REQUIRE(ret);
}
{
tl::expected<int, int> e = 21;
auto ret = std::move(e).map_error(ret_void);
REQUIRE(ret);
}
{
const tl::expected<int, int> e = 21;
auto ret = std::move(e).map_error(ret_void);
REQUIRE(ret);
}
{
tl::expected<int, int> e(tl::unexpect, 21);
auto ret = e.map_error(ret_void);
REQUIRE(!ret);
}
{
const tl::expected<int, int> e(tl::unexpect, 21);
auto ret = e.map_error(ret_void);
REQUIRE(!ret);
}
{
tl::expected<int, int> e(tl::unexpect, 21);
auto ret = std::move(e).map_error(ret_void);
REQUIRE(!ret);
}
{
const tl::expected<int, int> e(tl::unexpect, 21);
auto ret = std::move(e).map_error(ret_void);
REQUIRE(!ret);
}
} }
TEST_CASE("And then extensions", "[extensions.and_then]") { TEST_CASE("And then extensions", "[extensions.and_then]") {