gimo v0.1.0
Loading...
Searching...
No Matches
Common.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2025.
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_COMMON_HPP
7#define GIMO_COMMON_HPP
8
9#pragma once
10
11#include "gimo/Config.hpp"
12
13#include <concepts>
14#include <cstddef>
15#include <type_traits>
16#include <utility>
17
18namespace gimo::detail
19{
20 template <typename...>
21 struct always_false
22 : public std::bool_constant<false>
23 {
24 };
25
26 template <typename... Args>
27 inline constexpr bool always_false_v = always_false<Args...>::value;
28
29 template <std::size_t priority>
30 struct priority_tag
32 : public priority_tag<priority - 1u>
34 {
35 };
36
37 template <>
38 struct priority_tag<0u>
39 {
40 };
41
42 template <typename T, typename U>
43 struct const_ref_like
44 {
45 using type = std::conditional_t<
46 std::is_const_v<std::remove_reference_t<T>>,
47 std::remove_reference_t<U> const&&,
48 std::remove_reference_t<U>&&>;
49 };
50
51 template <typename T, typename U>
52 struct const_ref_like<T&, U>
53 {
54 using type = std::conditional_t<
55 std::is_const_v<std::remove_reference_t<T>>,
56 std::remove_reference_t<U> const&,
57 std::remove_reference_t<U>&>;
58 };
59
60 template <typename T, typename U>
61 using const_ref_like_t = const_ref_like<T, U>::type;
62
63 template <typename T, typename U>
64 [[nodiscard]]
65 constexpr auto&& forward_like(U&& x) noexcept
66 {
67 return static_cast<const_ref_like_t<T, U>>(x);
68 }
69
70 template <typename T>
71 concept unqualified = std::same_as<T, std::remove_cvref_t<T>>;
72
73 template <typename T>
74 concept transferable = !std::is_void_v<std::remove_cvref_t<T>>;
75
76 template <typename B>
77 concept boolean_testable =
78 std::convertible_to<B, bool>
79 && requires(B&& b) {
80 { !std::forward<B>(b) } -> std::convertible_to<bool>;
81 };
82
83 template <typename T, typename U>
84 concept weakly_equality_comparable_with =
85 requires(T const& t, U const& u) {
86 { t == u } -> boolean_testable;
87 { t != u } -> boolean_testable;
88 { u == t } -> boolean_testable;
89 { u != t } -> boolean_testable;
90 };
91}
92
93namespace gimo
94{
101 template <typename Nullable>
102 struct traits;
103
104 namespace detail
105 {
106 template <typename T, typename U, typename C = std::common_reference_t<T const&, U const&>>
107 concept regular_relationship_impl =
108 std::same_as<
109 std::common_reference_t<T const&, U const&>,
110 std::common_reference_t<U const&, T const&>>
111 && (std::convertible_to<T const&, C const&>
112 || std::convertible_to<T, C const&>)
113 && (std::convertible_to<U const&, C const&>
114 || std::convertible_to<U, C const&>);
115
116 template <typename T, typename U>
117 concept regular_relationship =
118 regular_relationship_impl<std::remove_cvref_t<T>, std::remove_cvref_t<U>>;
119
120 template <typename Lhs, typename Rhs>
121 concept weakly_assignable_from =
122 std::is_lvalue_reference_v<Lhs>
123 && requires(Lhs lhs, Rhs&& rhs) {
124 { lhs = std::forward<Rhs>(rhs) } -> std::same_as<Lhs>;
125 };
126 }
127
135 template <typename Null, typename Nullable>
136 concept null_for = detail::regular_relationship<Nullable, Null>
137 && detail::weakly_equality_comparable_with<Null, Nullable>
138 && std::constructible_from<Nullable, Null const&>
139 && detail::weakly_assignable_from<Nullable&, Null const&>;
140
141 namespace detail
142 {
143 template <typename T>
144 concept trait_readable_value = requires(T&& closure) {
145 { traits<std::remove_cvref_t<T>>::value(std::forward<T>(closure)) } -> transferable;
146 };
147
148 template <trait_readable_value T>
149 constexpr decltype(auto) value_impl([[maybe_unused]] priority_tag<2u> const tag, T&& closure)
150 {
151 return traits<std::remove_cvref_t<T>>::value(std::forward<T>(closure));
152 }
153
154 template <typename T>
155 concept indirectly_readable_value = requires(T&& closure) {
156 { *std::forward<T>(closure) } -> transferable;
157 };
158
159 template <indirectly_readable_value T>
160 constexpr decltype(auto) value_impl([[maybe_unused]] priority_tag<1u> const tag, T&& closure)
161 {
162 return *std::forward<T>(closure);
163 }
164
165 template <typename T>
166 concept adl_readable_value = requires(T&& closure) {
167 { value(std::forward<T>(closure)) } -> transferable;
168 };
169
170 template <adl_readable_value T>
171 constexpr decltype(auto) value_impl([[maybe_unused]] priority_tag<0u> const tag, T&& closure)
172 {
173 return value(std::forward<T>(closure));
174 }
175
176 inline constexpr priority_tag<2u> max_value_tag{};
177
178 template <typename T>
179 concept readable_value = requires(T&& closure) {
180 { detail::value_impl(max_value_tag, std::forward<T>(closure)) } -> transferable;
181 };
182
183 template <readable_value T>
184 constexpr decltype(auto) value(T&& closure)
185 {
186 return detail::value_impl(max_value_tag, std::forward<T>(closure));
187 }
188 }
189
199 template <typename T>
200 concept nullable = requires {
201 requires null_for<decltype(traits<std::remove_cvref_t<T>>::null), std::remove_cvref_t<T>>;
202 requires detail::readable_value<T>;
203 };
204
205 namespace detail
206 {
207 template <typename Nullable, typename Value>
208 concept trait_value_constructible = requires(Value&& value) {
209 { traits<Nullable>::from_value(std::forward<Value>(value)) } -> std::same_as<Nullable>;
210 };
211
212 template <typename Nullable, typename Arg>
213 requires trait_value_constructible<Nullable, Arg>
214 constexpr Nullable construct_from_value_impl([[maybe_unused]] priority_tag<1u> const tag, Arg&& arg)
215 noexcept(noexcept(traits<Nullable>::from_value(std::forward<Arg>(arg))))
216 {
217 return traits<Nullable>::from_value(std::forward<Arg>(arg));
218 }
219
220 template <typename Nullable, typename Arg>
221 requires std::constructible_from<Nullable, Arg&&>
222 constexpr Nullable construct_from_value_impl([[maybe_unused]] priority_tag<0u> const tag, Arg&& arg)
223 noexcept(std::is_nothrow_constructible_v<Nullable, Arg&&>)
224 {
225 return Nullable{std::forward<Arg>(arg)};
226 }
227
228 inline constexpr priority_tag<1u> max_value_constructible_tag{};
229
230 template <typename Nullable, typename Value>
231 concept constructible_from_value = requires(Value&& value) {
232 { detail::construct_from_value_impl<Nullable>(max_value_tag, std::forward<Value>(value)) } -> std::same_as<Nullable>;
233 };
234
235 template <typename Nullable, typename Value>
236 concept nothrow_constructible_from_value = requires(Value&& value) {
237 { detail::construct_from_value_impl<Nullable>(max_value_tag, std::forward<Value>(value)) } noexcept;
238 };
239 }
240
250 template <typename T, typename Arg>
252 && detail::unqualified<T>
253 && detail::constructible_from_value<T, Arg&&>;
254
267 template <nullable Nullable, typename Arg>
269 constexpr Nullable construct_from_value(Arg&& arg) noexcept(detail::nothrow_constructible_from_value<Nullable, Arg&&>)
270 {
271 return detail::construct_from_value_impl<Nullable>(detail::max_value_tag, std::forward<Arg>(arg));
272 }
273
279 template <nullable Nullable, typename Value>
280 using rebind_value_t = typename traits<std::remove_cvref_t<Nullable>>::template rebind_value<Value>;
281
287 template <typename Nullable, typename Value>
290 && requires {
292 { detail::value(std::declval<rebind_value_t<Nullable, Value>>()) } -> std::convertible_to<Value const&>;
293 };
294
299 template <nullable Nullable>
300 inline constexpr auto null_v{traits<std::remove_cvref_t<Nullable>>::null};
301
302 namespace detail
303 {
304 template <nullable Nullable>
305 using value_result_t = decltype(value(std::declval<Nullable&&>()));
306
307 template <typename Nullable>
308 [[nodiscard]]
309 constexpr bool has_value(Nullable const& target)
310 {
311 return target != null_v<Nullable>;
312 }
313
314 template <nullable T>
315 constexpr decltype(auto) forward_value(std::remove_reference_t<T>& nullable)
316 {
317 GIMO_ASSERT(detail::has_value(nullable), "Nullable must contain a value.", nullable);
318
319 return detail::value(std::forward<T>(nullable));
320 }
321
322 template <nullable Nullable>
323 [[nodiscard]]
324 constexpr auto construct_empty()
325 {
326 return Nullable{null_v<Nullable>};
327 }
328
329 template <nullable Nullable, nullable Source>
330 [[nodiscard]]
331 constexpr Nullable rebind_value(std::remove_reference_t<Source>& source)
332 {
333 return construct_from_value<Nullable>(forward_value<Source>(source));
334 }
335 }
336
337 namespace detail
338 {
339 template <typename T>
340 concept trait_readable_error = requires(T&& closure) {
341 { traits<std::remove_cvref_t<T>>::error(std::forward<T>(closure)) } -> transferable;
342 };
343
344 template <trait_readable_error T>
345 constexpr decltype(auto) error_impl([[maybe_unused]] priority_tag<2u> const tag, T&& closure)
346 {
347 return traits<std::remove_cvref_t<T>>::error(std::forward<T>(closure));
348 }
349
350 template <typename T>
351 concept member_readable_error = requires(T&& closure) {
352 { std::forward<T>(closure).error() } -> transferable;
353 };
354
355 template <member_readable_error T>
356 constexpr decltype(auto) error_impl([[maybe_unused]] priority_tag<1u> const tag, T&& closure)
357 {
358 return std::forward<T>(closure).error();
359 }
360
361 template <typename T>
362 concept adl_readable_error = requires(T&& closure) {
363 { error(std::forward<T>(closure)) } -> transferable;
364 };
365
366 template <adl_readable_error T>
367 constexpr decltype(auto) error_impl([[maybe_unused]] priority_tag<0u> const tag, T&& closure)
368 {
369 return error(std::forward<T>(closure));
370 }
371
372 inline constexpr priority_tag<2u> max_error_tag{};
373
374 template <typename T>
375 concept readable_error = requires(T&& closure) {
376 { detail::error_impl(max_error_tag, std::forward<T>(closure)) } -> transferable;
377 };
378
379 template <readable_error T>
380 constexpr decltype(auto) error(T&& closure)
381 {
382 return detail::error_impl(max_error_tag, std::forward<T>(closure));
383 }
384 }
385
393 template <typename T>
395 && detail::readable_error<T>;
396
402 template <typename T, typename Error>
405 && detail::unqualified<T>
406 && requires(Error&& e) {
407 { traits<T>::from_error(std::forward<Error>(e)) } -> std::same_as<T>;
408 };
409
415 template <expected_like Expected, typename Error>
416 using rebind_error_t = typename traits<std::remove_cvref_t<Expected>>::template rebind_error<std::remove_cvref_t<Error>>;
417
423 template <typename Expected, typename Error>
426 && requires {
428 { detail::error(std::declval<rebind_error_t<Expected, Error>&&>()) } -> std::convertible_to<Error const&>;
429 };
430
431 namespace detail
432 {
433 template <expected_like Expected>
434 using error_result_t = decltype(error(std::declval<Expected&&>()));
435
436 template <expected_like T>
437 constexpr decltype(auto) forward_error(std::remove_reference_t<T>& expected)
438 {
439 GIMO_ASSERT(!detail::has_value(expected), "Expected must hold an error.", expected);
440
441 return detail::error(std::forward<T>(expected));
442 }
443
444 template <expected_like Expected, typename Error>
445 constexpr Expected construct_from_error(Error&& error)
446 {
447 return traits<Expected>::from_error(std::forward<Error>(error));
448 }
449
450 template <expected_like Expected, expected_like Source>
451 [[nodiscard]]
452 constexpr Expected rebind_error(std::remove_reference_t<Source>& source)
453 {
454 return detail::construct_from_error<Expected>(forward_error<Source>(source));
455 }
456 }
457}
458
459#endif
#define GIMO_ASSERT(condition, msg,...)
Definition Config.hpp:11
Concept determining whether the Expected type supports rebinding its error-type.
Definition Common.hpp:403
Concept determining whether the Nullable is constructible with the specified argument.
Definition Common.hpp:251
Concept describing a type that acts like std::expected (has both a value and an error channel).
Definition Common.hpp:394
Concept describing the relationship between a Nullable type and its null state.
Definition Common.hpp:136
Concept describing a type that can be used as a monad in the pipeline.
Definition Common.hpp:200
Concept determining whether the Expected type supports rebinding its error-type.
Definition Common.hpp:424
Concept determining whether the Nullable type supports rebinding its value-type.
Definition Common.hpp:288
Definition AndThen.hpp:21
typename traits< std::remove_cvref_t< Expected > >::template rebind_error< std::remove_cvref_t< Error > > rebind_error_t
Helper alias to obtain the Expected type with the rebound Error type.
Definition Common.hpp:416
constexpr auto null_v
Helper to obtain the null value for a specific Nullable type.
Definition Common.hpp:300
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
The central customization point for the library.
Definition Common.hpp:102