Line data 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_TYPE_TRAITS_HPP
11 : #define BOOST_CAPY_TYPE_TRAITS_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 :
15 : #include <concepts>
16 : #include <cstddef>
17 : #include <tuple>
18 : #include <type_traits>
19 : #include <utility>
20 :
21 : namespace boost {
22 : namespace capy {
23 : namespace detail {
24 :
25 : struct any_type
26 : {
27 : template <typename T>
28 : constexpr operator T() const noexcept;
29 : };
30 :
31 : template <typename T, std::size_t N>
32 : concept is_tuple_n = requires {
33 : std::tuple_size<std::remove_cvref_t<T>>::value;
34 : } && std::tuple_size<std::remove_cvref_t<T>>::value == N;
35 :
36 : // clang-format off
37 : template <typename T>
38 : concept is_decomposable_1 =
39 : (std::is_aggregate_v<std::remove_cvref_t<T>> &&
40 : requires { std::remove_cvref_t<T>{ any_type{} }; } &&
41 : !requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; }
42 : ) || is_tuple_n<T, 1>;
43 :
44 : template <typename T>
45 : concept is_decomposable_2 =
46 : (std::is_aggregate_v<std::remove_cvref_t<T>> &&
47 : requires { std::remove_cvref_t<T>{ any_type{}, any_type{} }; } &&
48 : !requires { std::remove_cvref_t<T>{ any_type{}, any_type{}, any_type{} }; }
49 : ) || is_tuple_n<T, 2>;
50 :
51 : template <typename T>
52 : concept is_decomposable_3 =
53 : (std::is_aggregate_v<std::remove_cvref_t<T>> &&
54 : 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{}, any_type{} }; }
56 : ) || is_tuple_n<T, 3>;
57 :
58 : template <typename T>
59 : concept is_decomposable_4 =
60 : (std::is_aggregate_v<std::remove_cvref_t<T>> &&
61 : 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{}, any_type{} }; }
63 : ) || is_tuple_n<T, 4>;
64 :
65 : // clang-format on
66 :
67 : template <is_decomposable_1 T>
68 : auto decomposed_types(T&& t)
69 : {
70 : auto [v0] = t;
71 : return std::make_tuple(v0);
72 : }
73 :
74 : template <is_decomposable_2 T>
75 : auto decomposed_types(T&& t)
76 : {
77 : auto [v0, v1] = t;
78 : return std::make_tuple(v0, v1);
79 : }
80 :
81 : template <is_decomposable_3 T>
82 : auto decomposed_types(T&& t)
83 : {
84 : auto [v0, v1, v2] = t;
85 : return std::make_tuple(v0, v1, v2);
86 : }
87 :
88 : template <is_decomposable_4 T>
89 : auto decomposed_types(T&& t)
90 : {
91 : auto [v0, v1, v2, v3] = t;
92 : return std::make_tuple(v0, v1, v2, v3);
93 : }
94 :
95 : template <class T>
96 : std::tuple<> decomposed_types(T&&)
97 : {
98 : return {};
99 : }
100 :
101 : template<typename T>
102 0 : auto get_awaiter(T&& t)
103 : {
104 : if constexpr (requires { std::forward<T>(t).operator co_await(); })
105 : {
106 : return std::forward<T>(t).operator co_await();
107 : }
108 : else if constexpr (requires { operator co_await(std::forward<T>(t)); })
109 : {
110 : return operator co_await(std::forward<T>(t));
111 : }
112 : else
113 : {
114 0 : return std::forward<T>(t);
115 : }
116 : }
117 :
118 : template<typename A>
119 : using awaitable_return_t = decltype(
120 : get_awaiter(std::declval<A>()).await_resume()
121 : );
122 :
123 : template <typename T, typename... Types>
124 : concept decomposes_to = requires(T&& t) {
125 : { decomposed_types(std::forward<T>(t)) } -> std::same_as<std::tuple<Types...>>;
126 : };
127 :
128 : } // namespace detail
129 :
130 : /** Concept for awaitables whose return type decomposes to a specific typelist.
131 :
132 : A type satisfies `awaitable_decomposes_to` if it is an awaitable
133 : (has `await_resume`) and its return type decomposes to the
134 : specified typelist.
135 :
136 : @tparam A The awaitable type.
137 : @tparam Types The expected element types after decomposition.
138 :
139 : @par Requirements
140 : @li `A` must be an awaitable (directly or via `operator co_await`)
141 : @li The return type of `await_resume()` must decompose to `Types...`
142 :
143 : @par Example
144 : @code
145 : // Constrain a function to accept only awaitables that return
146 : // a decomposable result of (error_code, size_t)
147 : template<typename A>
148 : requires awaitable_decomposes_to<A, system::error_code, std::size_t>
149 : task<void> process(A&& op)
150 : {
151 : auto [ec, n] = co_await std::forward<A>(op);
152 : if (ec)
153 : co_return;
154 : // process n bytes...
155 : }
156 : @endcode
157 : */
158 : template<typename A, typename... Types>
159 : concept awaitable_decomposes_to = requires {
160 : typename detail::awaitable_return_t<A>;
161 : } && detail::decomposes_to<detail::awaitable_return_t<A>, Types...>;
162 :
163 : } // namespace capy
164 : } // namespace boost
165 :
166 : #endif
|