gimo v0.3.2
Loading...
Searching...
No Matches
Transform.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_TRANSFORM_HPP
7#define GIMO_ALGORITHM_TRANSFORM_HPP
8
9#pragma once
10
11#include "gimo/Common.hpp"
12#include "gimo/Pipeline.hpp"
14
15#include <functional>
16#include <tuple>
17#include <type_traits>
18#include <utility>
19
20namespace gimo::detail::transform
21{
22 template <typename Nullable, typename Action>
23 consteval Nullable* print_diagnostics()
24 {
25 if constexpr (!std::is_invocable_v<Action, value_result_t<Nullable>>)
26 {
27 static_assert(always_false_v<Nullable>, "The transform algorithm requires an action invocable with the nullable's value.");
28 }
29 else if constexpr (!rebindable_value_to<Nullable, std::invoke_result_t<Action, value_result_t<Nullable>>>)
30 {
31 static_assert(always_false_v<Nullable>, "The transform algorithm requires a nullable whose value-type can be rebound.");
32 }
33
34 return nullptr;
35 }
36
37 template <typename Nullable, typename Action>
38 using result_t = rebind_value_t<
39 Nullable,
40 std::invoke_result_t<Action, value_result_t<Nullable>>>;
41
42 template <typename Action, nullable Nullable>
43 [[nodiscard]]
44 constexpr result_t<Nullable, Action> on_value(Action&& action, Nullable&& opt)
45 {
47 std::invoke(
48 std::forward<Action>(action),
49 detail::forward_value<Nullable>(opt)));
50 }
51
52 template <typename Action, nullable Nullable, typename Next, typename... Steps>
53 [[nodiscard]]
54 constexpr auto on_value(
55 Action&& action,
56 Nullable&& opt,
57 Next&& next,
58 Steps&&... steps)
59 {
60 return std::forward<Next>(next).on_value(
61 transform::on_value(std::forward<Action>(action), std::forward<Nullable>(opt)),
62 std::forward<Steps>(steps)...);
63 }
64
65 template <typename Action, nullable Nullable>
66 [[nodiscard]]
67 constexpr result_t<Nullable, Action> on_null(Action&& /*action*/, Nullable&& /*opt*/)
68 {
69 return detail::construct_empty<result_t<Nullable, Action>>();
70 }
71
72 template <typename Action, expected_like Expected>
73 [[nodiscard]]
74 constexpr result_t<Expected, Action> on_null(Action&& /*action*/, Expected&& expected)
75 {
76 return detail::rebind_error<result_t<Expected, Action>, Expected>(expected);
77 }
78
79 template <nullable Nullable, typename Action, typename Next, typename... Steps>
80 [[nodiscard]]
81 constexpr auto on_null(Action&& action, Nullable&& opt, Next&& next, Steps&&... steps)
82 {
83 return std::forward<Next>(next).on_null(
84 transform::on_null(std::forward<Action>(action), std::forward<Nullable>(opt)),
85 std::forward<Steps>(steps)...);
86 }
87
88 struct traits
89 {
90 template <nullable Nullable, typename Action>
91 static constexpr bool is_applicable_on = requires {
92 requires rebindable_value_to<
93 Nullable,
94 std::invoke_result_t<Action, value_result_t<Nullable>>>;
95 };
96
97 template <typename Action, nullable Nullable, typename... Steps>
98 [[nodiscard]]
99 static constexpr auto on_value(Action&& action, Nullable&& opt, Steps&&... steps)
100 {
101 GIMO_ASSERT(detail::has_value(opt), "Nullable is empty while it's expected to contain a value.");
102
103 if constexpr (is_applicable_on<Nullable, Action>)
104 {
105 return transform::on_value(
106 std::forward<Action>(action),
107 std::forward<Nullable>(opt),
108 std::forward<Steps>(steps)...);
109 }
110 else
111 {
112 return *transform::print_diagnostics<Nullable, Action>();
113 }
114 }
115
116 template <typename Action, nullable Nullable, typename... Steps>
117 [[nodiscard]]
118 static constexpr auto on_null(Action&& action, Nullable&& opt, Steps&&... steps)
119 {
120 GIMO_ASSERT(!detail::has_value(opt), "Nullable contains a value while it's expected to be empty.");
121
122 if constexpr (is_applicable_on<Nullable, Action>)
123 {
124 return transform::on_null(
125 std::forward<Action>(action),
126 std::forward<Nullable>(opt),
127 std::forward<Steps>(steps)...);
128 }
129 else
130 {
131 return *transform::print_diagnostics<Nullable, Action>();
132 }
133 }
134 };
135}
136
137namespace gimo
138{
139 namespace detail
140 {
141 template <typename Action>
142 using transform_t = BasicAlgorithm<transform::traits, std::remove_cvref_t<Action>>;
143 }
144
164 template <typename Action>
165 [[nodiscard]]
166 constexpr auto transform(Action&& action)
167 {
168 using Algorithm = detail::transform_t<Action>;
169
170 return Pipeline{std::tuple<Algorithm>{std::forward<Action>(action)}};
171 }
172}
173
174#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 transform(Action &&action)
Creates a pipeline step that transforms the underlying value.
Definition Transform.hpp:166
Definition AndForward.hpp:21
constexpr Nullable construct_from_value(Arg &&arg) noexcept(detail::nothrow_constructible_from_value< Nullable, Arg && >)
Constructs the specified Nullable with the provided value.
Definition Common.hpp:269
typename traits< std::remove_cvref_t< Nullable > >::template rebind_value< Value > rebind_value_t
Helper alias to obtain the Nullable type with the rebound Value type.
Definition Common.hpp:280