Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 89 additions & 13 deletions include/tl/expected.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,7 @@ class expected : private detail::expected_move_assign_base<T, E>,
#endif
#endif

#ifndef TL_EXPECTED_STRICT
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
!defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
Expand Down Expand Up @@ -1376,6 +1377,7 @@ class expected : private detail::expected_move_assign_base<T, E>,
}
#endif
#endif
#endif

#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
!defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
Expand Down Expand Up @@ -1421,6 +1423,7 @@ class expected : private detail::expected_move_assign_base<T, E>,
#endif
#endif

#ifndef TL_EXPECTED_STRICT
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
!defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & {
Expand Down Expand Up @@ -1506,6 +1509,7 @@ class expected : private detail::expected_move_assign_base<T, E>,
return map_error_impl(std::move(*this), std::forward<F>(f));
}
#endif
#endif
#endif
template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
return or_else_impl(*this, std::forward<F>(f));
Expand All @@ -1523,6 +1527,49 @@ class expected : private detail::expected_move_assign_base<T, E>,
template <class F> expected constexpr or_else(F &&f) const && {
return or_else_impl(std::move(*this), std::forward<F>(f));
}
#endif
#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
!defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_or(F &&f) & {
return map_error_impl(*this, std::forward<F>(f));
}
template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_or(F &&f) && {
return map_error_impl(std::move(*this), std::forward<F>(f));
}
template <class F> constexpr auto transform_or(F &&f) const & {
return map_error_impl(*this, std::forward<F>(f));
}
template <class F> constexpr auto transform_or(F &&f) const && {
return map_error_impl(std::move(*this), std::forward<F>(f));
}
#else
template <class F>
TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
std::declval<F &&>()))
transform_or(F &&f) & {
return map_error_impl(*this, std::forward<F>(f));
}
template <class F>
TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
std::declval<F &&>()))
transform_or(F &&f) && {
return map_error_impl(std::move(*this), std::forward<F>(f));
}
template <class F>
constexpr decltype(map_error_impl(std::declval<const expected &>(),
std::declval<F &&>()))
transform_or(F &&f) const & {
return map_error_impl(*this, std::forward<F>(f));
}

#ifndef TL_EXPECTED_NO_CONSTRR
template <class F>
constexpr decltype(map_error_impl(std::declval<const expected &&>(),
std::declval<F &&>()))
transform_or(F &&f) const && {
return map_error_impl(std::move(*this), std::forward<F>(f));
}
#endif
#endif
constexpr expected() = default;
constexpr expected(const expected &rhs) = default;
Expand Down Expand Up @@ -2208,15 +2255,19 @@ auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
template <class Exp, class F,
detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
class Ret = decltype(detail::invoke(std::declval<F>(),
std::declval<Exp>().error())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
std::declval<Exp>().error()))
#ifndef TL_EXPECTED_STRICT
, detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr
#endif
>
constexpr auto map_error_impl(Exp &&exp, F &&f) {
using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
return exp.has_value()
? result(*std::forward<Exp>(exp))
: result(unexpect, detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error()));
}
#ifndef TL_EXPECTED_STRICT
template <class Exp, class F,
detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
class Ret = decltype(detail::invoke(std::declval<F>(),
Expand All @@ -2231,18 +2282,23 @@ auto map_error_impl(Exp &&exp, F &&f) {
detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
return result(unexpect, monostate{});
}
#endif
template <class Exp, class F,
detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
class Ret = decltype(detail::invoke(std::declval<F>(),
std::declval<Exp>().error())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
std::declval<Exp>().error()))
#ifndef TL_EXPECTED_STRICT
, detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr
#endif
>
constexpr auto map_error_impl(Exp &&exp, F &&f) {
using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
return exp.has_value()
? result()
: result(unexpect, detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error()));
}
#ifndef TL_EXPECTED_STRICT
template <class Exp, class F,
detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
class Ret = decltype(detail::invoke(std::declval<F>(),
Expand All @@ -2257,12 +2313,16 @@ auto map_error_impl(Exp &&exp, F &&f) {
detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
return result(unexpect, monostate{});
}
#endif
#else
template <class Exp, class F,
detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
class Ret = decltype(detail::invoke(std::declval<F>(),
std::declval<Exp>().error())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
std::declval<Exp>().error()))
#ifndef TL_EXPECTED_STRICT
, detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr
#endif
>
constexpr auto map_error_impl(Exp &&exp, F &&f)
-> expected<exp_t<Exp>, detail::decay_t<Ret>> {
using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
Expand All @@ -2273,6 +2333,7 @@ constexpr auto map_error_impl(Exp &&exp, F &&f)
std::forward<Exp>(exp).error()));
}

#ifndef TL_EXPECTED_STRICT
template <class Exp, class F,
detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
class Ret = decltype(detail::invoke(std::declval<F>(),
Expand All @@ -2287,12 +2348,16 @@ auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
return result(unexpect, monostate{});
}
#endif

template <class Exp, class F,
detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
class Ret = decltype(detail::invoke(std::declval<F>(),
std::declval<Exp>().error())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
std::declval<Exp>().error()))
#ifndef TL_EXPECTED_STRICT
, detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr
#endif
>
constexpr auto map_error_impl(Exp &&exp, F &&f)
-> expected<exp_t<Exp>, detail::decay_t<Ret>> {
using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
Expand All @@ -2303,6 +2368,7 @@ constexpr auto map_error_impl(Exp &&exp, F &&f)
std::forward<Exp>(exp).error()));
}

#ifndef TL_EXPECTED_STRICT
template <class Exp, class F,
detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
class Ret = decltype(detail::invoke(std::declval<F>(),
Expand All @@ -2318,19 +2384,23 @@ auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
return result(unexpect, monostate{});
}
#endif
#endif

#ifdef TL_EXPECTED_CXX14
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
std::declval<Exp>().error())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
std::declval<Exp>().error()))
#ifndef TL_EXPECTED_STRICT
, detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr
#endif
>
constexpr auto or_else_impl(Exp &&exp, F &&f) {
static_assert(detail::is_expected<Ret>::value, "F must return an expected");
return exp.has_value() ? std::forward<Exp>(exp)
: detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error());
}

#ifndef TL_EXPECTED_STRICT
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
std::declval<Exp>().error())),
Expand All @@ -2341,18 +2411,23 @@ detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
std::forward<Exp>(exp).error()),
std::forward<Exp>(exp));
}
#endif
#else
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
std::declval<Exp>().error())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
std::declval<Exp>().error()))
#ifndef TL_EXPECTED_STRICT
, detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr
#endif
>
auto or_else_impl(Exp &&exp, F &&f) -> Ret {
static_assert(detail::is_expected<Ret>::value, "F must return an expected");
return exp.has_value() ? std::forward<Exp>(exp)
: detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error());
}

#ifndef TL_EXPECTED_STRICT
template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
std::declval<Exp>().error())),
Expand All @@ -2364,6 +2439,7 @@ detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
std::forward<Exp>(exp));
}
#endif
#endif
} // namespace detail

template <class T, class E, class U, class F>
Expand Down
110 changes: 110 additions & 0 deletions tests/extensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,116 @@ TEST_CASE("Transform error extensions", "[extensions.transform_error]") {

}

TEST_CASE("transform_or", "[extensions.transform_or]") {
auto mul2 = [](int a) { return a * 2; };
auto ret_void = [](int a) { (void)a; };

{
tl::expected<int, int> e = 21;
auto ret = e.transform_or(mul2);
REQUIRE(ret);
REQUIRE(*ret == 21);
}

{
const tl::expected<int, int> e = 21;
auto ret = e.transform_or(mul2);
REQUIRE(ret);
REQUIRE(*ret == 21);
}

{
tl::expected<int, int> e = 21;
auto ret = std::move(e).transform_or(mul2);
REQUIRE(ret);
REQUIRE(*ret == 21);
}

{
const tl::expected<int, int> e = 21;
auto ret = std::move(e).transform_or(mul2);
REQUIRE(ret);
REQUIRE(*ret == 21);
}

{
tl::expected<int, int> e(tl::unexpect, 21);
auto ret = e.transform_or(mul2);
REQUIRE(!ret);
REQUIRE(ret.error() == 42);
}

{
const tl::expected<int, int> e(tl::unexpect, 21);
auto ret = e.transform_or(mul2);
REQUIRE(!ret);
REQUIRE(ret.error() == 42);
}

{
tl::expected<int, int> e(tl::unexpect, 21);
auto ret = std::move(e).transform_or(mul2);
REQUIRE(!ret);
REQUIRE(ret.error() == 42);
}

{
const tl::expected<int, int> e(tl::unexpect, 21);
auto ret = std::move(e).transform_or(mul2);
REQUIRE(!ret);
REQUIRE(ret.error() == 42);
}

{
tl::expected<int, int> e = 21;
auto ret = e.transform_or(ret_void);
REQUIRE(ret);
}

{
const tl::expected<int, int> e = 21;
auto ret = e.transform_or(ret_void);
REQUIRE(ret);
}

{
tl::expected<int, int> e = 21;
auto ret = std::move(e).transform_or(ret_void);
REQUIRE(ret);
}

{
const tl::expected<int, int> e = 21;
auto ret = std::move(e).transform_or(ret_void);
REQUIRE(ret);
}

{
tl::expected<int, int> e(tl::unexpect, 21);
auto ret = e.transform_or(ret_void);
REQUIRE(!ret);
}

{
const tl::expected<int, int> e(tl::unexpect, 21);
auto ret = e.transform_or(ret_void);
REQUIRE(!ret);
}

{
tl::expected<int, int> e(tl::unexpect, 21);
auto ret = std::move(e).transform_or(ret_void);
REQUIRE(!ret);
}

{
const tl::expected<int, int> e(tl::unexpect, 21);
auto ret = std::move(e).transform_or(ret_void);
REQUIRE(!ret);
}

}

struct S {
int x;
};
Expand Down