1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QTTYPETRAITS_H |
5 | #define QTTYPETRAITS_H |
6 | |
7 | #include <QtCore/qtconfigmacros.h> |
8 | #include <QtCore/qtdeprecationmarkers.h> |
9 | |
10 | #include <optional> |
11 | #include <tuple> |
12 | #include <type_traits> |
13 | #include <utility> |
14 | #include <variant> |
15 | |
16 | #if 0 |
17 | #pragma qt_class(QtTypeTraits) |
18 | #pragma qt_sync_stop_processing |
19 | #endif |
20 | |
21 | QT_BEGIN_NAMESPACE |
22 | |
23 | // like std::to_underlying |
24 | template <typename Enum> |
25 | constexpr std::underlying_type_t<Enum> qToUnderlying(Enum e) noexcept |
26 | { |
27 | return static_cast<std::underlying_type_t<Enum>>(e); |
28 | } |
29 | |
30 | #ifndef QT_NO_QASCONST |
31 | #if QT_DEPRECATED_SINCE(6, 6) |
32 | |
33 | // this adds const to non-const objects (like std::as_const) |
34 | template <typename T> |
35 | QT_DEPRECATED_VERSION_X_6_6("Use std::as_const() instead." ) |
36 | constexpr typename std::add_const<T>::type &qAsConst(T &t) noexcept { return t; } |
37 | // prevent rvalue arguments: |
38 | template <typename T> |
39 | void qAsConst(const T &&) = delete; |
40 | |
41 | #endif // QT_DEPRECATED_SINCE(6, 6) |
42 | #endif // QT_NO_QASCONST |
43 | |
44 | #ifndef QT_NO_QEXCHANGE |
45 | |
46 | // like std::exchange |
47 | template <typename T, typename U = T> |
48 | constexpr T qExchange(T &t, U &&newValue) |
49 | noexcept(std::conjunction_v<std::is_nothrow_move_constructible<T>, |
50 | std::is_nothrow_assignable<T &, U>>) |
51 | { |
52 | T old = std::move(t); |
53 | t = std::forward<U>(newValue); |
54 | return old; |
55 | } |
56 | |
57 | #endif // QT_NO_QEXCHANGE |
58 | |
59 | namespace QtPrivate { |
60 | // helper to be used to trigger a "dependent static_assert(false)" |
61 | // (for instance, in a final `else` branch of a `if constexpr`.) |
62 | template <typename T> struct type_dependent_false : std::false_type {}; |
63 | template <auto T> struct value_dependent_false : std::false_type {}; |
64 | } |
65 | |
66 | namespace QTypeTraits { |
67 | |
68 | namespace detail { |
69 | template<typename T, typename U, |
70 | typename = std::enable_if_t<std::is_arithmetic_v<T> && std::is_arithmetic_v<U> && |
71 | std::is_floating_point_v<T> == std::is_floating_point_v<U> && |
72 | std::is_signed_v<T> == std::is_signed_v<U> && |
73 | !std::is_same_v<T, bool> && !std::is_same_v<U, bool> && |
74 | !std::is_same_v<T, char> && !std::is_same_v<U, char>>> |
75 | struct Promoted |
76 | { |
77 | using type = decltype(T() + U()); |
78 | }; |
79 | } |
80 | |
81 | template <typename T, typename U> |
82 | using Promoted = typename detail::Promoted<T, U>::type; |
83 | |
84 | /* |
85 | The templates below aim to find out whether one can safely instantiate an operator==() or |
86 | operator<() for a type. |
87 | |
88 | This is tricky for containers, as most containers have unconstrained comparison operators, even though they |
89 | rely on the corresponding operators for its content. |
90 | This is especially true for all of the STL template classes that have a comparison operator defined, and |
91 | leads to the situation, that the compiler would try to instantiate the operator, and fail if any |
92 | of its template arguments does not have the operator implemented. |
93 | |
94 | The code tries to cover the relevant cases for Qt and the STL, by checking (recusrsively) the value_type |
95 | of a container (if it exists), and checking the template arguments of pair, tuple and variant. |
96 | */ |
97 | namespace detail { |
98 | |
99 | // find out whether T is a conteiner |
100 | // this is required to check the value type of containers for the existence of the comparison operator |
101 | template <typename, typename = void> |
102 | struct is_container : std::false_type {}; |
103 | template <typename T> |
104 | struct is_container<T, std::void_t< |
105 | typename T::value_type, |
106 | std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool> |
107 | >> : std::true_type {}; |
108 | |
109 | |
110 | // Checks the existence of the comparison operator for the class itself |
111 | QT_WARNING_PUSH |
112 | QT_WARNING_DISABLE_FLOAT_COMPARE |
113 | template <typename, typename = void> |
114 | struct has_operator_equal : std::false_type {}; |
115 | template <typename T> |
116 | struct has_operator_equal<T, std::void_t<decltype(bool(std::declval<const T&>() == std::declval<const T&>()))>> |
117 | : std::true_type {}; |
118 | QT_WARNING_POP |
119 | |
120 | // Two forward declarations |
121 | template<typename T, bool = is_container<T>::value> |
122 | struct expand_operator_equal_container; |
123 | template<typename T> |
124 | struct expand_operator_equal_tuple; |
125 | |
126 | // the entry point for the public method |
127 | template<typename T> |
128 | using expand_operator_equal = expand_operator_equal_container<T>; |
129 | |
130 | // if T isn't a container check if it's a tuple like object |
131 | template<typename T, bool> |
132 | struct expand_operator_equal_container : expand_operator_equal_tuple<T> {}; |
133 | // if T::value_type exists, check first T::value_type, then T itself |
134 | template<typename T> |
135 | struct expand_operator_equal_container<T, true> : |
136 | std::conjunction< |
137 | std::disjunction< |
138 | std::is_same<T, typename T::value_type>, // avoid endless recursion |
139 | expand_operator_equal<typename T::value_type> |
140 | >, expand_operator_equal_tuple<T>> {}; |
141 | |
142 | // recursively check the template arguments of a tuple like object |
143 | template<typename ...T> |
144 | using expand_operator_equal_recursive = std::conjunction<expand_operator_equal<T>...>; |
145 | |
146 | template<typename T> |
147 | struct expand_operator_equal_tuple : has_operator_equal<T> {}; |
148 | template<typename T> |
149 | struct expand_operator_equal_tuple<std::optional<T>> : expand_operator_equal_recursive<T> {}; |
150 | template<typename T1, typename T2> |
151 | struct expand_operator_equal_tuple<std::pair<T1, T2>> : expand_operator_equal_recursive<T1, T2> {}; |
152 | template<typename ...T> |
153 | struct expand_operator_equal_tuple<std::tuple<T...>> : expand_operator_equal_recursive<T...> {}; |
154 | template<typename ...T> |
155 | struct expand_operator_equal_tuple<std::variant<T...>> : expand_operator_equal_recursive<T...> {}; |
156 | |
157 | // the same for operator<(), see above for explanations |
158 | template <typename, typename = void> |
159 | struct has_operator_less_than : std::false_type{}; |
160 | template <typename T> |
161 | struct has_operator_less_than<T, std::void_t<decltype(bool(std::declval<const T&>() < std::declval<const T&>()))>> |
162 | : std::true_type{}; |
163 | |
164 | template<typename T, bool = is_container<T>::value> |
165 | struct expand_operator_less_than_container; |
166 | template<typename T> |
167 | struct expand_operator_less_than_tuple; |
168 | |
169 | template<typename T> |
170 | using expand_operator_less_than = expand_operator_less_than_container<T>; |
171 | |
172 | template<typename T, bool> |
173 | struct expand_operator_less_than_container : expand_operator_less_than_tuple<T> {}; |
174 | template<typename T> |
175 | struct expand_operator_less_than_container<T, true> : |
176 | std::conjunction< |
177 | std::disjunction< |
178 | std::is_same<T, typename T::value_type>, |
179 | expand_operator_less_than<typename T::value_type> |
180 | >, expand_operator_less_than_tuple<T> |
181 | > {}; |
182 | |
183 | template<typename ...T> |
184 | using expand_operator_less_than_recursive = std::conjunction<expand_operator_less_than<T>...>; |
185 | |
186 | template<typename T> |
187 | struct expand_operator_less_than_tuple : has_operator_less_than<T> {}; |
188 | template<typename T> |
189 | struct expand_operator_less_than_tuple<std::optional<T>> : expand_operator_less_than_recursive<T> {}; |
190 | template<typename T1, typename T2> |
191 | struct expand_operator_less_than_tuple<std::pair<T1, T2>> : expand_operator_less_than_recursive<T1, T2> {}; |
192 | template<typename ...T> |
193 | struct expand_operator_less_than_tuple<std::tuple<T...>> : expand_operator_less_than_recursive<T...> {}; |
194 | template<typename ...T> |
195 | struct expand_operator_less_than_tuple<std::variant<T...>> : expand_operator_less_than_recursive<T...> {}; |
196 | |
197 | } |
198 | |
199 | template<typename T, typename = void> |
200 | struct is_dereferenceable : std::false_type {}; |
201 | |
202 | template<typename T> |
203 | struct is_dereferenceable<T, std::void_t<decltype(std::declval<T>().operator->())> > |
204 | : std::true_type {}; |
205 | |
206 | template <typename T> |
207 | inline constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value; |
208 | |
209 | template<typename T> |
210 | struct has_operator_equal : detail::expand_operator_equal<T> {}; |
211 | template<typename T> |
212 | inline constexpr bool has_operator_equal_v = has_operator_equal<T>::value; |
213 | |
214 | template <typename Container, typename T> |
215 | using has_operator_equal_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_equal<T>>; |
216 | |
217 | template<typename T> |
218 | struct has_operator_less_than : detail::expand_operator_less_than<T> {}; |
219 | template<typename T> |
220 | inline constexpr bool has_operator_less_than_v = has_operator_less_than<T>::value; |
221 | |
222 | template <typename Container, typename T> |
223 | using has_operator_less_than_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_less_than<T>>; |
224 | |
225 | template <typename ...T> |
226 | using compare_eq_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal<T>...>, bool>; |
227 | |
228 | template <typename Container, typename ...T> |
229 | using compare_eq_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal_container<Container, T>...>, bool>; |
230 | |
231 | template <typename ...T> |
232 | using compare_lt_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than<T>...>, bool>; |
233 | |
234 | template <typename Container, typename ...T> |
235 | using compare_lt_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than_container<Container, T>...>, bool>; |
236 | |
237 | namespace detail { |
238 | |
239 | template<typename T> |
240 | const T &const_reference(); |
241 | template<typename T> |
242 | T &reference(); |
243 | |
244 | } |
245 | |
246 | template <typename Stream, typename, typename = void> |
247 | struct has_ostream_operator : std::false_type {}; |
248 | template <typename Stream, typename T> |
249 | struct has_ostream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() << detail::const_reference<T>())>> |
250 | : std::true_type {}; |
251 | template <typename Stream, typename T> |
252 | inline constexpr bool has_ostream_operator_v = has_ostream_operator<Stream, T>::value; |
253 | |
254 | template <typename Stream, typename Container, typename T> |
255 | using has_ostream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_ostream_operator<Stream, T>>; |
256 | |
257 | template <typename Stream, typename, typename = void> |
258 | struct has_istream_operator : std::false_type {}; |
259 | template <typename Stream, typename T> |
260 | struct has_istream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() >> detail::reference<T>())>> |
261 | : std::true_type {}; |
262 | template <typename Stream, typename T> |
263 | inline constexpr bool has_istream_operator_v = has_istream_operator<Stream, T>::value; |
264 | template <typename Stream, typename Container, typename T> |
265 | using has_istream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_istream_operator<Stream, T>>; |
266 | |
267 | template <typename Stream, typename T> |
268 | inline constexpr bool has_stream_operator_v = has_ostream_operator_v<Stream, T> && has_istream_operator_v<Stream, T>; |
269 | |
270 | } // namespace QTypeTraits |
271 | |
272 | QT_END_NAMESPACE |
273 | |
274 | #endif // QTTYPETRAITS_H |
275 | |