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

Commit fd89269

Browse files
committed
add monads to Result class
1 parent b269fa6 commit fd89269

1 file changed

Lines changed: 366 additions & 1 deletion

File tree

include/common/result.hpp

Lines changed: 366 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,31 @@ class SentinelValue<T> {
8383
static constexpr T value = get(); ///< Precomputed sentinel value for type T.
8484
};
8585

86+
// forward declarations
87+
template<typename T, typename... Errs>
88+
requires(sizeof...(Errs) > 0) && (std::derived_from<Errs, ResultError> && ...)
89+
class Result;
90+
91+
template<typename... Errs>
92+
requires(sizeof...(Errs) > 0) && (std::derived_from<Errs, ResultError> && ...)
93+
class Result<void, Errs...>;
94+
95+
/**
96+
* @brief Type trait to check if a type is a Result.
97+
* @tparam T Type to check.
98+
*/
99+
template<typename>
100+
struct is_result : std::false_type {};
101+
102+
template<typename T, typename... Errs>
103+
struct is_result<Result<T, Errs...>> : std::true_type {};
104+
105+
template<typename... Errs>
106+
struct is_result<Result<void, Errs...>> : std::true_type {};
107+
108+
template<typename T>
109+
inline constexpr bool is_result_v = is_result<T>::value;
110+
86111
/**
87112
* @brief Result class for expected value or error handling (similar to std::expected).
88113
* @tparam T Type of the expected value.
@@ -196,7 +221,175 @@ class Result {
196221
}
197222

198223
constexpr bool has_error() {
199-
return !std::holds_alternative<std::monostate>(m_value);
224+
return !std::holds_alternative<std::monostate>(m_error);
225+
}
226+
227+
// and_then: lvalue
228+
template<typename Fn>
229+
auto and_then(Fn&& f) & {
230+
using ResultType = std::invoke_result_t<Fn, T>;
231+
static_assert(is_result_v<ResultType>, "Fn must return a Result type");
232+
233+
if (has_error()) {
234+
return std::visit([](auto&& err) -> ResultType {
235+
return ResultType(std::forward<decltype(err)>(err));
236+
}, m_error);
237+
} else {
238+
return f(m_value);
239+
}
240+
}
241+
242+
// and_then: const lvalue
243+
template<typename Fn>
244+
auto and_then(Fn&& f) const& {
245+
using ResultType = std::invoke_result_t<Fn, const T&>;
246+
static_assert(is_result_v<ResultType>, "Fn must return a Result type");
247+
248+
if (has_error()) {
249+
return std::visit([](auto&& err) -> ResultType {
250+
return ResultType(std::forward<decltype(err)>(err));
251+
}, m_error);
252+
} else {
253+
return f(m_value);
254+
}
255+
}
256+
257+
// and_then: rvalue
258+
template<typename Fn>
259+
auto and_then(Fn&& f) && {
260+
using ResultType = std::invoke_result_t<Fn, T&&>;
261+
static_assert(is_result_v<ResultType>, "Fn must return a Result type");
262+
263+
if (has_error()) {
264+
return std::visit([](auto&& err) -> ResultType {
265+
return ResultType(std::forward<decltype(err)>(err));
266+
}, std::move(m_error));
267+
} else {
268+
return f(std::move(m_value));
269+
}
270+
}
271+
272+
// and_then: const rvalue
273+
template<typename Fn>
274+
auto and_then(Fn&& f) const&& {
275+
using ResultType = std::invoke_result_t<Fn, const T&&>;
276+
static_assert(is_result_v<ResultType>, "Fn must return a Result type");
277+
278+
if (has_error()) {
279+
return std::visit([](auto&& err) -> ResultType {
280+
return ResultType(std::forward<decltype(err)>(err));
281+
}, std::move(m_error));
282+
} else {
283+
return f(std::move(m_value));
284+
}
285+
}
286+
287+
// or_else: lvalue
288+
template<typename Fn>
289+
auto or_else(Fn&& f) & {
290+
if (!has_error()) {
291+
return *this;
292+
}
293+
294+
return std::visit([&f](auto&& err) {
295+
return f(std::forward<decltype(err)>(err));
296+
}, m_error);
297+
}
298+
299+
// or_else: const lvalue
300+
template<typename Fn>
301+
auto or_else(Fn&& f) const& {
302+
if (!has_error()) {
303+
return *this;
304+
}
305+
306+
return std::visit([&f](auto&& err) {
307+
return f(std::forward<decltype(err)>(err));
308+
}, m_error);
309+
}
310+
311+
// or_else: rvalue
312+
template<typename Fn>
313+
auto or_else(Fn&& f) && {
314+
if (!has_error()) {
315+
return std::move(*this);
316+
}
317+
318+
return std::visit([&f](auto&& err) {
319+
return f(std::forward<decltype(err)>(err));
320+
}, std::move(m_error));
321+
}
322+
323+
// or_else: const rvalue
324+
template<typename Fn>
325+
auto or_else(Fn&& f) const&& {
326+
if (!has_error()) {
327+
return std::move(*this);
328+
}
329+
330+
return std::visit([&f](auto&& err) {
331+
return f(std::forward<decltype(err)>(err));
332+
}, std::move(m_error));
333+
}
334+
335+
// transform: lvalue
336+
template<typename Fn>
337+
auto transform(Fn&& f) & {
338+
using U = std::invoke_result_t<Fn, T>;
339+
using ResultType = Result<U, Errs...>;
340+
341+
if (has_error()) {
342+
return std::visit([](auto&& err) -> ResultType {
343+
return ResultType(std::forward<decltype(err)>(err));
344+
}, m_error);
345+
} else {
346+
return ResultType(f(m_value));
347+
}
348+
}
349+
350+
// transform: const lvalue
351+
template<typename Fn>
352+
auto transform(Fn&& f) const& {
353+
using U = std::invoke_result_t<Fn, const T&>;
354+
using ResultType = Result<U, Errs...>;
355+
356+
if (has_error()) {
357+
return std::visit([](auto&& err) -> ResultType {
358+
return ResultType(std::forward<decltype(err)>(err));
359+
}, m_error);
360+
} else {
361+
return ResultType(f(m_value));
362+
}
363+
}
364+
365+
// transform: rvalue
366+
template<typename Fn>
367+
auto transform(Fn&& f) && {
368+
using U = std::invoke_result_t<Fn, T&&>;
369+
using ResultType = Result<U, Errs...>;
370+
371+
if (has_error()) {
372+
return std::visit([](auto&& err) -> ResultType {
373+
return ResultType(std::forward<decltype(err)>(err));
374+
}, std::move(m_error));
375+
} else {
376+
return ResultType(f(std::move(m_value)));
377+
}
378+
}
379+
380+
// transform: const rvalue
381+
template<typename Fn>
382+
auto transform(Fn&& f) const&& {
383+
using U = std::invoke_result_t<Fn, const T&&>;
384+
using ResultType = Result<U, Errs...>;
385+
386+
if (has_error()) {
387+
return std::visit([](auto&& err) -> ResultType {
388+
return ResultType(std::forward<decltype(err)>(err));
389+
}, std::move(m_error));
390+
} else {
391+
return ResultType(f(std::move(m_value)));
392+
}
200393
}
201394

202395
constexpr operator T&() & {
@@ -309,6 +502,178 @@ class Result<void, Errs...> {
309502
}
310503
}
311504

505+
constexpr bool has_error() {
506+
return !std::holds_alternative<std::monostate>(m_error);
507+
}
508+
509+
// and_then: lvalue
510+
template<typename Fn>
511+
auto and_then(Fn&& f) & {
512+
using ResultType = std::invoke_result_t<Fn>;
513+
static_assert(is_result_v<ResultType>, "Fn must return a Result type");
514+
515+
if (has_error()) {
516+
return std::visit([](auto&& err) -> ResultType {
517+
return ResultType(std::forward<decltype(err)>(err));
518+
}, m_error);
519+
} else {
520+
return f();
521+
}
522+
}
523+
524+
// and_then: const lvalue
525+
template<typename Fn>
526+
auto and_then(Fn&& f) const& {
527+
using ResultType = std::invoke_result_t<Fn>;
528+
static_assert(is_result_v<ResultType>, "Fn must return a Result type");
529+
530+
if (has_error()) {
531+
return std::visit([](auto&& err) -> ResultType {
532+
return ResultType(std::forward<decltype(err)>(err));
533+
}, m_error);
534+
} else {
535+
return f();
536+
}
537+
}
538+
539+
// and_then: rvalue
540+
template<typename Fn>
541+
auto and_then(Fn&& f) && {
542+
using ResultType = std::invoke_result_t<Fn>;
543+
static_assert(is_result_v<ResultType>, "Fn must return a Result type");
544+
545+
if (has_error()) {
546+
return std::visit([](auto&& err) -> ResultType {
547+
return ResultType(std::forward<decltype(err)>(err));
548+
}, std::move(m_error));
549+
} else {
550+
return f();
551+
}
552+
}
553+
554+
// and_then: const rvalue
555+
template<typename Fn>
556+
auto and_then(Fn&& f) const&& {
557+
using ResultType = std::invoke_result_t<Fn>;
558+
static_assert(is_result_v<ResultType>, "Fn must return a Result type");
559+
560+
if (has_error()) {
561+
return std::visit([](auto&& err) -> ResultType {
562+
return ResultType(std::forward<decltype(err)>(err));
563+
}, std::move(m_error));
564+
} else {
565+
return f();
566+
}
567+
}
568+
569+
// or_else: lvalue
570+
template<typename Fn>
571+
auto or_else(Fn&& f) & {
572+
if (!has_error()) {
573+
return *this;
574+
}
575+
576+
return std::visit([&f](auto&& err) {
577+
return f(std::forward<decltype(err)>(err));
578+
}, m_error);
579+
}
580+
581+
// or_else: const lvalue
582+
template<typename Fn>
583+
auto or_else(Fn&& f) const& {
584+
if (!has_error()) {
585+
return *this;
586+
}
587+
588+
return std::visit([&f](auto&& err) {
589+
return f(std::forward<decltype(err)>(err));
590+
}, m_error);
591+
}
592+
593+
// or_else: rvalue
594+
template<typename Fn>
595+
auto or_else(Fn&& f) && {
596+
if (!has_error()) {
597+
return std::move(*this);
598+
}
599+
600+
return std::visit([&f](auto&& err) {
601+
return f(std::forward<decltype(err)>(err));
602+
}, std::move(m_error));
603+
}
604+
605+
// or_else: const rvalue
606+
template<typename Fn>
607+
auto or_else(Fn&& f) const&& {
608+
if (!has_error()) {
609+
return std::move(*this);
610+
}
611+
612+
return std::visit([&f](auto&& err) {
613+
return f(std::forward<decltype(err)>(err));
614+
}, std::move(m_error));
615+
}
616+
617+
// transform: lvalue
618+
template<typename Fn>
619+
auto transform(Fn&& f) & {
620+
using U = std::invoke_result_t<Fn>;
621+
using ResultType = Result<U, Errs...>;
622+
623+
if (has_error()) {
624+
return std::visit([](auto&& err) -> ResultType {
625+
return ResultType(std::forward<decltype(err)>(err));
626+
}, m_error);
627+
} else {
628+
return ResultType(f());
629+
}
630+
}
631+
632+
// transform: const lvalue
633+
template<typename Fn>
634+
auto transform(Fn&& f) const& {
635+
using U = std::invoke_result_t<Fn>;
636+
using ResultType = Result<U, Errs...>;
637+
638+
if (has_error()) {
639+
return std::visit([](auto&& err) -> ResultType {
640+
return ResultType(std::forward<decltype(err)>(err));
641+
}, m_error);
642+
} else {
643+
return ResultType(f());
644+
}
645+
}
646+
647+
// transform: rvalue
648+
template<typename Fn>
649+
auto transform(Fn&& f) && {
650+
using U = std::invoke_result_t<Fn>;
651+
using ResultType = Result<U, Errs...>;
652+
653+
if (has_error()) {
654+
return std::visit([](auto&& err) -> ResultType {
655+
return ResultType(std::forward<decltype(err)>(err));
656+
}, std::move(m_error));
657+
} else {
658+
return ResultType(f());
659+
}
660+
}
661+
662+
// transform: const rvalue
663+
template<typename Fn>
664+
auto transform(Fn&& f) const&& {
665+
using U = std::invoke_result_t<Fn>;
666+
using ResultType = Result<U, Errs...>;
667+
668+
if (has_error()) {
669+
return std::visit([](auto&& err) -> ResultType {
670+
return ResultType(std::forward<decltype(err)>(err));
671+
}, std::move(m_error));
672+
} else {
673+
return ResultType(f());
674+
}
675+
}
676+
312677
private:
313678
// instead of wrapping the variant in std::optional, we can use std::monostate
314679
std::variant<std::monostate, Errs...> m_error; ///< Variant holding an error or monostate.

0 commit comments

Comments
 (0)