libs/capy/include/boost/capy/concept/decomposes_to.hpp

0.0% Lines (0/2) -% Functions (0/1) -% Branches (0/0)
libs/capy/include/boost/capy/concept/decomposes_to.hpp
Line Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/capy
8 //
9
10 #ifndef BOOST_CAPY_DECOMPOSES_TO_HPP
11 #define BOOST_CAPY_DECOMPOSES_TO_HPP
12
13 #include <boost/capy/detail/config.hpp>
14
15 #include <system_error>
16 #include <concepts>
17 #include <cstddef>
18 #include <tuple>
19 #include <type_traits>
20 #include <utility>
21
22 namespace boost {
23 namespace capy {
24 namespace detail {
25
26 struct any_type
27 {
28 template <typename T>
29 constexpr operator T() const noexcept;
30 };
31
32 template <typename T, std::size_t N>
33 concept is_tuple_n = requires {
34 std::tuple_size<std::remove_cvref_t<T>>::value;
35 } && std::tuple_size<std::remove_cvref_t<T>>::value == N;
36
37 // clang-format off
38 template <typename T>
39 concept is_decomposable_1 =
40 (std::is_aggregate_v<std::remove_cvref_t<T>> &&
41 requires { std::remove_cvref_t<T>{ any_type{} }; } &&
42 !requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; }
43 ) || is_tuple_n<T, 1>;
44
45 template <typename T>
46 concept is_decomposable_2 =
47 (std::is_aggregate_v<std::remove_cvref_t<T>> &&
48 requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; } &&
49 !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{} }; }
50 ) || is_tuple_n<T, 2>;
51
52 template <typename T>
53 concept is_decomposable_3 =
54 (std::is_aggregate_v<std::remove_cvref_t<T>> &&
55 requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{} }; } &&
56 !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{} }; }
57 ) || is_tuple_n<T, 3>;
58
59 template <typename T>
60 concept is_decomposable_4 =
61 (std::is_aggregate_v<std::remove_cvref_t<T>> &&
62 requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{} }; } &&
63 !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{}, any_type{}, any_type{} }; }
64 ) || is_tuple_n<T, 4>;
65
66 // clang-format on
67
68 template <is_decomposable_1 T>
69 auto decomposed_types(T&& t)
70 {
71 auto [v0] = t;
72 return std::make_tuple(v0);
73 }
74
75 template <is_decomposable_2 T>
76 auto decomposed_types(T&& t)
77 {
78 auto [v0, v1] = t;
79 return std::make_tuple(v0, v1);
80 }
81
82 template <is_decomposable_3 T>
83 auto decomposed_types(T&& t)
84 {
85 auto [v0, v1, v2] = t;
86 return std::make_tuple(v0, v1, v2);
87 }
88
89 template <is_decomposable_4 T>
90 auto decomposed_types(T&& t)
91 {
92 auto [v0, v1, v2, v3] = t;
93 return std::make_tuple(v0, v1, v2, v3);
94 }
95
96 template <class T>
97 std::tuple<> decomposed_types(T&&)
98 {
99 return {};
100 }
101
102 template<typename T>
103 auto get_awaiter(T&& t)
104 {
105 if constexpr (requires { std::forward<T>(t).operator co_await(); })
106 {
107 return std::forward<T>(t).operator co_await();
108 }
109 else if constexpr (requires { operator co_await(std::forward<T>(t)); })
110 {
111 return operator co_await(std::forward<T>(t));
112 }
113 else
114 {
115 return std::forward<T>(t);
116 }
117 }
118
119 template<typename A>
120 using awaitable_return_t = decltype(
121 get_awaiter(std::declval<A>()).await_resume()
122 );
123
124 } // namespace detail
125
126 /** Concept for types that decompose to a specific typelist.
127
128 A type satisfies `decomposes_to` if it can be decomposed via
129 structured bindings into the specified types. This includes
130 aggregates with matching member types and tuple-like types
131 with matching element types.
132
133 @tparam T The type to decompose.
134 @tparam Types The expected element types after decomposition.
135
136 @par Example
137 @code
138 struct result { int a; double b; };
139
140 static_assert(decomposes_to<result, int, double>);
141 static_assert(decomposes_to<std::tuple<int, double>, int, double>);
142 @endcode
143 */
144 template <typename T, typename... Types>
145 concept decomposes_to = requires(T&& t) {
146 { detail::decomposed_types(std::forward<T>(t)) } -> std::same_as<std::tuple<Types...>>;
147 };
148
149 /** Concept for awaitables whose return type decomposes to a specific typelist.
150
151 A type satisfies `awaitable_decomposes_to` if it is an awaitable
152 (has `await_resume`) and its return type decomposes to the
153 specified typelist.
154
155 @tparam A The awaitable type.
156 @tparam Types The expected element types after decomposition.
157
158 @par Requirements
159 @li `A` must be an awaitable (directly or via `operator co_await`)
160 @li The return type of `await_resume()` must decompose to `Types...`
161
162 @par Example
163 @code
164 // Constrain a function to accept only awaitables that return
165 // a decomposable result of (error_code, size_t)
166 template<typename A>
167 requires awaitable_decomposes_to<A, std::error_code, std::size_t>
168 task<void> process(A&& op)
169 {
170 auto [ec, n] = co_await std::forward<A>(op);
171 if (ec)
172 co_return;
173 // process n bytes...
174 }
175 @endcode
176 */
177 template<typename A, typename... Types>
178 concept awaitable_decomposes_to = requires {
179 typename detail::awaitable_return_t<A>;
180 } && decomposes_to<detail::awaitable_return_t<A>, Types...>;
181
182 } // namespace capy
183 } // namespace boost
184
185 #endif
186