Skip to content
This repository was archived by the owner on May 3, 2026. It is now read-only.

Commit 156078f

Browse files
committed
update Result<void>::or_else to match primary template
1 parent 5af751d commit 156078f

1 file changed

Lines changed: 70 additions & 27 deletions

File tree

include/common/result.hpp

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ class Result {
497497
return std::visit([](auto&& arg) -> traits::and_then_return_t<Self, F> {
498498
// even though this conditional is impossible, it's necessary to stop the compiler
499499
// from thinking that ReturnType could be constructed with std::monostate
500-
if constexpr (std::is_same_v<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
500+
if constexpr (std::same_as<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
501501
throw std::logic_error("This exception is unreachable");
502502
} else {
503503
return std::forward<decltype(arg)>(arg);
@@ -511,6 +511,9 @@ class Result {
511511
/**
512512
* @brief or_else monadic function.
513513
*
514+
* @note wrappers are used for the requires clause in order to avoid the use of fold
515+
* expressions, as fold expressions significantly complicates error messages
516+
*
514517
* constraints:
515518
* - Callable must be invocable with at least one error type as an argument
516519
* - If the callable is invocable, it must always return the same type given different parameter
@@ -535,12 +538,14 @@ class Result {
535538
// Result instance
536539
&& traits::AllValueTypeMatch<Self, traits::or_else_return_t<Self, F, Errs>...>
537540
constexpr auto or_else(this Self&& self, F&& f) {
541+
using namespace traits;
542+
538543
// the return type is the same as the return type of the callable, unless the callable
539544
// returns void. In that case, the return type is Self with cv-refs removed
540545
using CallableReturnType =
541-
traits::first_type_that_satisfies_t<traits::invocable_indirect_v<F, Self>, Errs...>;
546+
first_type_that_satisfies_t<invocable_indirect_v<F, Self>, Errs...>;
542547
using ReturnType = std::conditional_t<
543-
traits::IsResult<CallableReturnType>,
548+
IsResult<CallableReturnType>,
544549
CallableReturnType,
545550
std::remove_cvref_t<Self>>;
546551

@@ -555,7 +560,7 @@ class Result {
555560
// even though this condition is impossible, it's necessary. Otherwise the
556561
// compiler will compile a branch where f is invoked with an unsupported argument
557562
// type
558-
if constexpr (!traits::Invocable<Self, F, decltype((arg))>
563+
if constexpr (!Invocable<Self, F, decltype((arg))>
559564
|| std::
560565
is_same_v<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
561566
throw std::logic_error("This exception is unreachable");
@@ -571,7 +576,7 @@ class Result {
571576
// even though this condition is impossible, it's necessary. Otherwise the
572577
// compiler will compile a branch where f is invoked with an unsupported argument
573578
// type
574-
if constexpr (!traits::Invocable<Self, F, decltype((arg))>
579+
if constexpr (!Invocable<Self, F, decltype((arg))>
575580
|| std::is_same_v<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
576581
throw std::logic_error("This exception is unreachable");
577582
} else {
@@ -624,12 +629,6 @@ operator==(const Result<LhsT, LhsErrs...>& lhs, const Result<RhsT, RhsErrs...>&
624629
template<traits::IsResultError... Errs>
625630
requires(sizeof...(Errs) > 0)
626631
class Result<void, Errs...> {
627-
private:
628-
// or_else return type helper
629-
template<typename Self, typename F, typename E>
630-
using or_else_return_t =
631-
std::invoke_result_t<F, decltype((std::get<E>(std::declval<Self>().error)))>;
632-
633632
public:
634633
using value_type = void;
635634
using error_types = traits::type_pack<Errs...>;
@@ -717,33 +716,77 @@ class Result<void, Errs...> {
717716
/**
718717
* @brief or_else monadic function.
719718
*
720-
* The callable must be able to take a perfectly forwarded error that may be contained by this
721-
* Result instance. The callable must be able to return a Result containing any error type that
722-
* may be passed to it. The normal value type of the Result returned by the callable must be the
723-
* same as the normal value type of this Result instance.
719+
* @note wrappers are used for the requires clause in order to avoid the use of fold
720+
* expressions, as fold expressions significantly complicates error messages
721+
*
722+
* constraints:
723+
* - Callable must be invocable with at least one error type as an argument
724+
* - If the callable is invocable, it must always return the same type given different parameter
725+
* types
726+
* - The callable is only allowed to return a Result, or void
727+
* - If a Result is returned, it must have the same value type as this Result instance
728+
* - The return type of the callable must be the same for all possible arguments it may be
729+
* called with.
724730
*
725731
* @tparam Self deduced self type
726732
* @tparam F the type of the callable
727733
* @param self the current Result instance
728734
* @param f the callable
729735
*/
730736
template<typename Self, typename F>
731-
requires(std::invocable<F, decltype((std::get<Errs>(std::declval<Self>().error)))> && ...)
732-
&& (traits::IsResult<or_else_return_t<Self, F, Errs>> && ...)
733-
&& (traits::AllSame<or_else_return_t<Self, F, Errs>...>)
734-
&& (std::same_as<void, typename or_else_return_t<Self, F, Errs>::value_type> && ...)
737+
requires traits::AnyInvocable<Self, F, Errs...>
738+
// the callable must always return the same type (if it's invocable)
739+
&& traits::AllSame<traits::or_else_return_t<Self, F, Errs>...>
740+
// the callable must return a Result or void (if it's invocable)
741+
&& traits::AllResultOrVoid<Self, F, Errs...>
742+
// if the callable returns a Result, it must have the same value type as this
743+
// Result instance
744+
&& traits::AllValueTypeMatch<Self, traits::or_else_return_t<Self, F, Errs>...>
735745
constexpr auto or_else(this Self&& self, F&& f) {
736-
using ReturnType = or_else_return_t<Self, F, std::tuple_element_t<0, std::tuple<Errs...>>>;
737-
// if there isn't an error, return
738-
return ReturnType();
739-
// otherwise, invoke the given lambda
746+
using namespace traits;
747+
748+
// the return type is the same as the return type of the callable, unless the callable
749+
// returns void. In that case, the return type is Self with cv-refs removed
750+
using CallableReturnType =
751+
first_type_that_satisfies_t<invocable_indirect_v<F, Self>, Errs...>;
752+
using ReturnType = std::conditional_t<
753+
IsResult<CallableReturnType>,
754+
CallableReturnType,
755+
std::remove_cvref_t<Self>>;
756+
757+
// if there isn't an error, return the value
758+
if (!self.has_error()) {
759+
return ReturnType(std::forward<Self>(self).value);
760+
}
761+
762+
// if the callable returns void
763+
if constexpr (std::same_as<void, CallableReturnType>) {
764+
return std::visit([&f](auto&& arg) -> ReturnType {
765+
// even though this condition is impossible, it's necessary. Otherwise the
766+
// compiler will compile a branch where f is invoked with an unsupported argument
767+
// type
768+
if constexpr (!Invocable<Self, F, decltype((arg))>
769+
|| std::
770+
is_same_v<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
771+
throw std::logic_error("This exception is unreachable");
772+
} else {
773+
std::invoke(f, std::forward<decltype(arg)>(arg));
774+
return std::forward<decltype(arg)>(arg);
775+
}
776+
}, std::forward<Self>(self).error);
777+
}
778+
779+
// if the callable returns Result
740780
return std::visit([&f](auto&& arg) -> ReturnType {
741-
// even though this conditional is impossible, it's necessary to stop the compiler from
742-
// thinking that ReturnType could be constructed with std::monostate
743-
if constexpr (std::is_same_v<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
781+
// even though this condition is impossible, it's necessary. Otherwise the
782+
// compiler will compile a branch where f is invoked with an unsupported argument
783+
// type
784+
if constexpr (!Invocable<Self, F, decltype((arg))>
785+
|| std::is_same_v<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
744786
throw std::logic_error("This exception is unreachable");
745787
} else {
746-
return std::invoke(f, arg);
788+
std::invoke(f, std::forward<decltype(arg)>(arg));
789+
return std::forward<decltype(arg)>(arg);
747790
}
748791
}, std::forward<Self>(self).error);
749792
}

0 commit comments

Comments
 (0)