GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 0.0% 0 / 0 / 2
Functions: -% 0 / 1 / 1
Branches: -% 0 / 0 / 0

libs/capy/include/boost/capy/type_traits.hpp
Line Branch Exec Source
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 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 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
167