gimo v0.3.2
Loading...
Searching...
No Matches
OrElse.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2025-2026.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef GIMO_ALGORITHM_OR_ELSE_HPP
7#define GIMO_ALGORITHM_OR_ELSE_HPP
8
9#pragma once
10
11#include "gimo/Common.hpp"
12#include "gimo/Pipeline.hpp"
14
15#include <concepts>
16#include <functional>
17#include <tuple>
18#include <type_traits>
19#include <utility>
20
21namespace gimo::detail::or_else
22{
23 template <typename Nullable, typename Action>
24 consteval Nullable& print_diagnostics()
25 {
26 if constexpr (!std::is_invocable_v<Action>)
27 {
28 static_assert(always_false_v<Nullable>, "The or_else algorithm requires an action invocable without any arguments.");
29 }
30 else if constexpr (!std::same_as<Nullable, std::invoke_result_t<Action>> && !std::is_void_v<std::invoke_result_t<Action>>)
31 {
32 static_assert(always_false_v<Nullable>, "The or_else algorithm requires an action returning the same nullable type or void.");
33 }
34
35 return std::declval<Nullable&>();
36 }
37
38 template <typename Action, nullable Nullable>
39 [[nodiscard]]
40 constexpr std::remove_cvref_t<Nullable> on_value(Action&& /*action*/, Nullable&& opt)
41 {
42 return std::forward<Nullable>(opt);
43 }
44
45 template <typename Action, nullable Nullable, typename Next, typename... Steps>
46 [[nodiscard]]
47 constexpr auto on_value(
48 Action&& /*action*/,
49 Nullable&& opt,
50 Next&& next,
51 Steps&&... steps)
52 {
53 return std::forward<Next>(next).on_value(
54 std::forward<Nullable>(opt),
55 std::forward<Steps>(steps)...);
56 }
57
58 template <typename Action, nullable Nullable>
59 [[nodiscard]]
60 constexpr std::remove_cvref_t<Nullable> on_null(Action&& action, Nullable&& /*opt*/)
61 {
62 if constexpr (std::is_void_v<std::invoke_result_t<Action>>)
63 {
64 std::invoke(std::forward<Action>(action));
65 return detail::construct_empty<std::remove_cvref_t<Nullable>>();
66 }
67 else
68 {
69 return std::invoke(std::forward<Action>(action));
70 }
71 }
72
73 template <nullable Nullable, typename Action, typename Next, typename... Steps>
74 [[nodiscard]]
75 constexpr auto on_null(Action&& action, Nullable&& opt, Next&& next, Steps&&... steps)
76 {
77 return std::invoke(
78 std::forward<Next>(next),
79 or_else::on_null(std::forward<Action>(action), std::forward<Nullable>(opt)),
80 std::forward<Steps>(steps)...);
81 }
82
83 template <nullable Nullable, typename Action, typename Next, typename... Steps>
84 requires std::is_void_v<std::invoke_result_t<Action>>
85 [[nodiscard]]
86 constexpr auto on_null(Action&& action, Nullable&& opt, Next&& next, Steps&&... steps)
87 {
88 return std::forward<Next>(next).on_null(
89 or_else::on_null(std::forward<Action>(action), std::forward<Nullable>(opt)),
90 std::forward<Steps>(steps)...);
91 }
92
93 struct traits
94 {
95 template <nullable Nullable, typename Action>
96 static constexpr bool is_applicable_on = requires {
97 requires std::same_as<
98 std::remove_cvref_t<Nullable>,
99 std::remove_cvref_t<std::invoke_result_t<Action>>>
100 || std::is_void_v<std::invoke_result_t<Action>>;
101 };
102
103 template <typename Action, nullable Nullable, typename... Steps>
104 [[nodiscard]]
105 static constexpr auto on_value(Action&& action, Nullable&& opt, Steps&&... steps)
106 {
107 GIMO_ASSERT(detail::has_value(opt), "Nullable is empty while it's expected to contain a value.");
108
109 if constexpr (is_applicable_on<Nullable, Action>)
110 {
111 return or_else::on_value(
112 std::forward<Action>(action),
113 std::forward<Nullable>(opt),
114 std::forward<Steps>(steps)...);
115 }
116 else
117 {
118 return or_else::print_diagnostics<Nullable, Action>();
119 }
120 }
121
122 template <typename Action, nullable Nullable, typename... Steps>
123 [[nodiscard]]
124 static constexpr auto on_null(Action&& action, Nullable&& opt, Steps&&... steps)
125 {
126 GIMO_ASSERT(!detail::has_value(opt), "Nullable contains a value while it's expected to be empty.");
127
128 if constexpr (is_applicable_on<Nullable, Action>)
129 {
130 return or_else::on_null(
131 std::forward<Action>(action),
132 std::forward<Nullable>(opt),
133 std::forward<Steps>(steps)...);
134 }
135 else
136 {
137 return or_else::print_diagnostics<Nullable, Action>();
138 }
139 }
140 };
141}
142
143namespace gimo
144{
145 namespace detail
146 {
147 template <typename Action>
148 using or_else_t = BasicAlgorithm<or_else::traits, std::remove_cvref_t<Action>>;
149 }
150
162 template <typename Action>
163 [[nodiscard]]
164 constexpr auto or_else(Action&& action)
165 {
166 using Algorithm = detail::or_else_t<Action>;
167
168 return Pipeline{std::tuple<Algorithm>{std::forward<Action>(action)}};
169 }
170}
171
172#endif
#define GIMO_ASSERT(condition, msg,...)
Definition Config.hpp:11
A composite object representing a sequence of monadic operations.
Definition Pipeline.hpp:30
constexpr auto or_else(Action &&action)
Creates a pipeline step that handles the null/error case.
Definition OrElse.hpp:164
Definition AndForward.hpp:21