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