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