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