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