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 :
24 : /** Concept for types that support the tuple protocol.
25 :
26 : A type satisfies `has_tuple_protocol` if `std::tuple_size<T>`
27 : is a complete type with a `value` member.
28 :
29 : @tparam T The type to check.
30 : */
31 : template<typename T>
32 : concept has_tuple_protocol = requires {
33 : std::tuple_size<std::remove_cvref_t<T>>::value;
34 : };
35 :
36 : namespace detail {
37 :
38 : template<typename T, std::size_t... Is>
39 : auto decomposed_types_impl(std::index_sequence<Is...>)
40 : -> std::tuple<std::tuple_element_t<Is, std::remove_cvref_t<T>>...>;
41 :
42 : template<typename T>
43 : requires has_tuple_protocol<T>
44 : using decomposed_types_t = decltype(
45 : decomposed_types_impl<T>(
46 : std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<T>>>{}
47 : )
48 : );
49 :
50 : template<typename T>
51 0 : auto get_awaiter(T&& t)
52 : {
53 : if constexpr (requires { std::forward<T>(t).operator co_await(); })
54 : {
55 : return std::forward<T>(t).operator co_await();
56 : }
57 : else if constexpr (requires { operator co_await(std::forward<T>(t)); })
58 : {
59 : return operator co_await(std::forward<T>(t));
60 : }
61 : else
62 : {
63 0 : return std::forward<T>(t);
64 : }
65 : }
66 :
67 : template<typename A>
68 : using awaitable_return_t = decltype(
69 : get_awaiter(std::declval<A>()).await_resume()
70 : );
71 :
72 : } // namespace detail
73 :
74 : /** Concept for types that decompose to a specific typelist.
75 :
76 : A type satisfies `decomposes_to` if it supports structured bindings
77 : via the tuple protocol and its element types match the specified
78 : typelist exactly.
79 :
80 : @tparam T The type to check.
81 : @tparam Types The expected element types after decomposition.
82 :
83 : @par Requirements
84 : @li `T` must satisfy the tuple protocol (`std::tuple_size`,
85 : `std::tuple_element`)
86 : @li The number of elements must equal `sizeof...(Types)`
87 : @li Each element type must match the corresponding type in `Types`
88 :
89 : @par Example
90 : @code
91 : static_assert(decomposes_to<std::pair<int, double>, int, double>);
92 : static_assert(decomposes_to<std::tuple<int, float, char>, int, float, char>);
93 : static_assert(decomposes_to<std::array<int, 3>, int, int, int>);
94 :
95 : // Constrain a function template
96 : template<typename T>
97 : requires decomposes_to<T, system::error_code, std::size_t>
98 : void process_result(T&& result)
99 : {
100 : auto [ec, n] = std::forward<T>(result);
101 : // ...
102 : }
103 : @endcode
104 :
105 : @note Plain aggregates without the tuple protocol are not supported.
106 : Use `std::pair`, `std::tuple`, `std::array`, or add the tuple
107 : protocol to your type.
108 : */
109 : template<typename T, typename... Types>
110 : concept decomposes_to =
111 : has_tuple_protocol<T> &&
112 : std::same_as<
113 : detail::decomposed_types_t<T>,
114 : std::tuple<Types...>
115 : >;
116 :
117 : /** Concept for awaitables whose return type decomposes to a specific typelist.
118 :
119 : A type satisfies `awaitable_decomposes_to` if it is an awaitable
120 : (has `await_resume`) and its return type satisfies @ref decomposes_to
121 : with the specified typelist.
122 :
123 : @tparam A The awaitable type.
124 : @tparam Types The expected element types after decomposition.
125 :
126 : @par Requirements
127 : @li `A` must be an awaitable (directly or via `operator co_await`)
128 : @li The return type of `await_resume()` must satisfy @ref decomposes_to
129 : with `Types...`
130 :
131 : @par Example
132 : @code
133 : // Constrain a function to accept only awaitables that return
134 : // a decomposable result of (error_code, size_t)
135 : template<typename A>
136 : requires awaitable_decomposes_to<A, system::error_code, std::size_t>
137 : task<void> process(A&& op)
138 : {
139 : auto [ec, n] = co_await std::forward<A>(op);
140 : if (ec)
141 : co_return;
142 : // process n bytes...
143 : }
144 : @endcode
145 :
146 : @see decomposes_to
147 : */
148 : template<typename A, typename... Types>
149 : concept awaitable_decomposes_to = requires {
150 : typename detail::awaitable_return_t<A>;
151 : } && decomposes_to<detail::awaitable_return_t<A>, Types...>;
152 :
153 : } // namespace capy
154 : } // namespace boost
155 :
156 : #endif
|