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

Commit d35d7ac

Browse files
committed
improve Result::and_then
1 parent 7b7743c commit d35d7ac

1 file changed

Lines changed: 28 additions & 22 deletions

File tree

include/common/result.hpp

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ concept IsResult = is_result_v<T>;
214214
template<traits::HasSentinel T, traits::IsResultError... Errs>
215215
requires(sizeof...(Errs) > 0)
216216
class Result {
217+
private:
218+
// helper type
219+
template<typename Self, typename F>
220+
using and_then_return_t = std::invoke_result_t<F, decltype((std::declval<Self>().m_value))>;
221+
217222
public:
218223
// instead of wrapping the variant in std::optional, we can use std::monostate
219224
std::variant<std::monostate, Errs...> error;
@@ -288,28 +293,26 @@ class Result {
288293
/**
289294
* @brief and_then monadic function
290295
*
291-
* The callable must be able to return any error passed to it.
292-
* The callable must return a Result.
296+
* The callable must be able to take the perfectly-forwarded value of this Result instance as an
297+
* argument. The callable must be able to return any error passed to it. The callable must
298+
* return a Result.
293299
*
294300
* @tparam Self deduced self type
295301
* @tparam F the type of the callable
296302
* @param self the current Result instance
297303
* @param f the callable
298304
* @return return type of callable
299305
*/
300-
template<typename Self, std::invocable<Self> F>
306+
template<typename Self, typename F>
301307
constexpr auto and_then(this Self&& self, F&& f)
302-
requires traits::is_result_v<
303-
std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>>
304-
&& traits::contains_all_v<
305-
std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>,
306-
typename Self::error_types>
308+
requires std::invocable<F, decltype(std::forward<Self>(self).m_value)>
309+
&& traits::is_result_v<and_then_return_t<Self, F>>
310+
&& traits::contains_all_v<and_then_return_t<Self, F>, typename Self::error_types>
307311
{
308-
using ReturnType = std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>;
309312
// if there is an error, return said error immediately
310313
if (self.has_error()) {
311-
return std::visit([](auto&& var) {
312-
return ReturnType(std::forward<decltype(var)>(var));
314+
return std::visit([](auto&& var) -> and_then_return_t<Self, F> {
315+
return std::forward<decltype(var)>(var);
313316
}, std::forward<Self>(self));
314317
}
315318
// otherwise, invoke the callable and return the result
@@ -359,6 +362,11 @@ operator==(const Result<LhsT, LhsErrs...>& lhs, const Result<RhsT, RhsErrs...>&
359362
template<traits::IsResultError... Errs>
360363
requires(sizeof...(Errs) > 0)
361364
class Result<void, Errs...> {
365+
private:
366+
// helper type
367+
template<typename Self, typename F>
368+
using and_then_return_t = std::invoke_result_t<F, decltype((std::declval<Self>().m_value))>;
369+
362370
public:
363371
/**
364372
* @brief Construct a Result with an error.
@@ -407,28 +415,26 @@ class Result<void, Errs...> {
407415
/**
408416
* @brief and_then monadic function
409417
*
410-
* The callable must be able to return any error passed to it.
411-
* The callable must return a Result.
418+
* The callable must be able to take the perfectly-forwarded value of this Result instance as an
419+
* argument. The callable must be able to return any error passed to it. The callable must
420+
* return a Result.
412421
*
413422
* @tparam Self deduced self type
414423
* @tparam F the type of the callable
415424
* @param self the current Result instance
416425
* @param f the callable
417426
* @return return type of callable
418427
*/
419-
template<typename Self, std::invocable<Self> F>
428+
template<typename Self, typename F>
420429
constexpr auto and_then(this Self&& self, F&& f)
421-
requires traits::is_result_v<
422-
std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>>
423-
&& traits::contains_all_v<
424-
std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>,
425-
typename Self::error_types>
430+
requires std::invocable<F, decltype(std::forward<Self>(self).m_value)>
431+
&& traits::is_result_v<and_then_return_t<Self, F>>
432+
&& traits::contains_all_v<and_then_return_t<Self, F>, typename Self::error_types>
426433
{
427-
using ReturnType = std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>;
428434
// if there is an error, return said error immediately
429435
if (self.has_error()) {
430-
return std::visit([](auto&& var) {
431-
return ReturnType(std::forward<decltype(var)>(var));
436+
return std::visit([](auto&& var) -> and_then_return_t<Self, F> {
437+
return std::forward<decltype(var)>(var);
432438
}, std::forward<Self>(self));
433439
}
434440
// otherwise, invoke the callable and return the result

0 commit comments

Comments
 (0)