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

Commit 5e610ae

Browse files
committed
Result::or_else callable must always be invocable
1 parent a787198 commit 5e610ae

2 files changed

Lines changed: 15 additions & 64 deletions

File tree

include/common/result.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ concept Invocable = std::invocable<F, decltype((std::get<E>(std::declval<R>().er
280280
* @tparam Es the possible argument types passed to the callable
281281
*/
282282
template<typename R, typename F, typename... Es>
283-
concept AnyInvocable = (Invocable<R, F, Es> || ...);
283+
concept AllInvocable = (Invocable<R, F, Es> && ...);
284284

285285
/**
286286
* @brief check whether a callable is invocable given the Result and argument type

include/common/result_impl.hpp

Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class Result {
131131
* expressions, as fold expressions significantly complicates error messages
132132
*
133133
* constraints:
134-
* - Callable must be invocable with at least one error type as an argument
134+
* - Callable must be invocable with all error types as arguments
135135
* - If invocable, the callable must always return the same type regardless of argument type
136136
* - If invocable, the callable must return a Result
137137
* - The Result returned by the callable must have the same value type
@@ -142,53 +142,29 @@ class Result {
142142
* @param f the callable
143143
*/
144144
template<typename Self, typename F>
145-
requires traits::AnyInvocable<Self, F, Errs...>
145+
requires traits::AllInvocable<Self, F, Errs...>
146146
// the callable must always return the same type (if it's invocable)
147147
&& traits::AllSame<traits::or_else_return_t<Self, F, Errs>...>
148148
// the callable must return a Result (if it's invocable)
149149
&& traits::AllResult<Self, F, Errs...>
150150
// the Result returned by the callable must have the same value type
151151
&& traits::AllValueTypeMatch<Self, traits::or_else_return_t<Self, F, Errs>...>
152152
constexpr auto or_else(this Self&& self, F&& f) {
153-
using namespace traits;
154-
155-
// the return type is the same as the return type of the callable, unless the callable
156-
// returns void. In that case, the return type is Self with cv-refs removed
157-
using CallableReturnType =
158-
first_type_that_satisfies_t<invocable_indirect_v<F, Self>, Errs...>;
159-
using ReturnType = std::conditional_t<
160-
IsResult<CallableReturnType>,
161-
CallableReturnType,
162-
std::remove_cvref_t<Self>>;
153+
using ReturnType = std::invoke_result_t<
154+
F,
155+
decltype((std::get<std::tuple_element_t<0, std::tuple<Errs...>>>(std::declval<Self>()
156+
.error)))>;
163157

164158
// if there isn't an error, return the value
165159
if (!self.has_error()) {
166160
return ReturnType(std::forward<Self>(self).value);
167161
}
168162

169-
// if the callable returns void
170-
if constexpr (std::same_as<void, CallableReturnType>) {
171-
return std::visit([&f](auto&& arg) -> ReturnType {
172-
// even though this condition is impossible, it's necessary. Otherwise the
173-
// compiler will compile a branch where f is invoked with an unsupported argument
174-
// type
175-
if constexpr (!Invocable<Self, F, decltype((arg))>
176-
|| std::same_as<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
177-
throw std::logic_error("This exception is unreachable");
178-
} else {
179-
std::invoke(f, std::forward<decltype(arg)>(arg));
180-
return std::forward<decltype(arg)>(arg);
181-
}
182-
}, std::forward<Self>(self).error);
183-
}
184-
185-
// if the callable returns Result
186163
return std::visit([&f](auto&& arg) -> ReturnType {
187164
// even though this condition is impossible, it's necessary. Otherwise the
188165
// compiler will compile a branch where f is invoked with an unsupported argument
189166
// type
190-
if constexpr (!Invocable<Self, F, decltype((arg))>
191-
|| std::same_as<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
167+
if constexpr (std::same_as<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
192168
throw std::logic_error("This exception is unreachable");
193169
} else {
194170
std::invoke(f, std::forward<decltype(arg)>(arg));
@@ -331,7 +307,7 @@ class Result<void, Errs...> {
331307
* expressions, as fold expressions significantly complicates error messages
332308
*
333309
* constraints:
334-
* - Callable must be invocable with at least one error type as an argument
310+
* - Callable must be invocable with any error type as an argument
335311
* - If invocable, the callable must always return the same type regardless of argument type
336312
* - If invocable, the callable must return a Result
337313
* - The Result returned by the callable must have the same value type
@@ -342,53 +318,28 @@ class Result<void, Errs...> {
342318
* @param f the callable
343319
*/
344320
template<typename Self, typename F>
345-
requires traits::AnyInvocable<Self, F, Errs...>
321+
requires traits::AllInvocable<Self, F, Errs...>
346322
// the callable must always return the same type (if it's invocable)
347323
&& traits::AllSame<traits::or_else_return_t<Self, F, Errs>...>
348324
// the callable must return a Result (if it's invocable)
349325
&& traits::AllResult<Self, F, Errs...>
350326
// the Result returned by the callable must have the same value type
351327
&& traits::AllValueTypeMatch<Self, traits::or_else_return_t<Self, F, Errs>...>
352328
constexpr auto or_else(this Self&& self, F&& f) {
353-
using namespace traits;
354-
355-
// the return type is the same as the return type of the callable, unless the callable
356-
// returns void. In that case, the return type is Self with cv-refs removed
357-
using CallableReturnType =
358-
first_type_that_satisfies_t<invocable_indirect_v<F, Self>, Errs...>;
359-
using ReturnType = std::conditional_t<
360-
IsResult<CallableReturnType>,
361-
CallableReturnType,
362-
std::remove_cvref_t<Self>>;
363-
329+
using ReturnType = std::invoke_result_t<
330+
F,
331+
decltype((std::get<std::tuple_element_t<0, std::tuple<Errs...>>>(std::declval<Self>()
332+
.error)))>;
364333
// if there isn't an error, return
365334
if (!self.has_error()) {
366335
return ReturnType();
367336
}
368337

369-
// if the callable returns void
370-
if constexpr (std::same_as<void, CallableReturnType>) {
371-
return std::visit([&f](auto&& arg) -> ReturnType {
372-
// even though this condition is impossible, it's necessary. Otherwise the
373-
// compiler will compile a branch where f is invoked with an unsupported argument
374-
// type
375-
if constexpr (!Invocable<Self, F, decltype((arg))>
376-
|| std::same_as<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
377-
throw std::logic_error("This exception is unreachable");
378-
} else {
379-
std::invoke(f, std::forward<decltype(arg)>(arg));
380-
return std::forward<decltype(arg)>(arg);
381-
}
382-
}, std::forward<Self>(self).error);
383-
}
384-
385-
// if the callable returns Result
386338
return std::visit([&f](auto&& arg) -> ReturnType {
387339
// even though this condition is impossible, it's necessary. Otherwise the
388340
// compiler will compile a branch where f is invoked with an unsupported argument
389341
// type
390-
if constexpr (!Invocable<Self, F, decltype((arg))>
391-
|| std::same_as<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
342+
if constexpr (std::same_as<std::monostate, std::remove_cvref_t<decltype(arg)>>) {
392343
throw std::logic_error("This exception is unreachable");
393344
} else {
394345
std::invoke(f, std::forward<decltype(arg)>(arg));

0 commit comments

Comments
 (0)