1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // Copyright (C) 2016 Intel Corporation. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #ifndef QTYPEINFO_H |
6 | #define QTYPEINFO_H |
7 | |
8 | #include <QtCore/qcompilerdetection.h> |
9 | #include <QtCore/qcontainerfwd.h> |
10 | |
11 | #include <variant> |
12 | #include <optional> |
13 | #include <tuple> |
14 | #include <type_traits> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | class QDebug; |
19 | |
20 | /* |
21 | QTypeInfo - type trait functionality |
22 | */ |
23 | |
24 | namespace QtPrivate { |
25 | |
26 | template <typename T> |
27 | inline constexpr bool qIsRelocatable = std::is_trivially_copyable_v<T> && std::is_trivially_destructible_v<T>; |
28 | |
29 | // Denotes types that are trivially default constructible, and for which |
30 | // value-initialization can be achieved by filling their storage with 0 bits. |
31 | // There is no type trait we can use for this, so we hardcode a list of |
32 | // possibilities that we know are OK on the architectures that we support. |
33 | // The most notable exception are pointers to data members, which for instance |
34 | // on the Itanium ABI are initialized to -1. |
35 | template <typename T> |
36 | inline constexpr bool qIsValueInitializationBitwiseZero = |
37 | std::is_scalar_v<T> && !std::is_member_object_pointer_v<T>; |
38 | |
39 | } |
40 | |
41 | /* |
42 | The catch-all template. |
43 | */ |
44 | |
45 | template <typename T> |
46 | class QTypeInfo |
47 | { |
48 | public: |
49 | enum { |
50 | isPointer [[deprecated("Use std::is_pointer instead" )]] = std::is_pointer_v<T>, |
51 | isIntegral [[deprecated("Use std::is_integral instead" )]] = std::is_integral_v<T>, |
52 | isComplex = !std::is_trivial_v<T>, |
53 | isRelocatable = QtPrivate::qIsRelocatable<T>, |
54 | isValueInitializationBitwiseZero = QtPrivate::qIsValueInitializationBitwiseZero<T>, |
55 | }; |
56 | }; |
57 | |
58 | template<> |
59 | class QTypeInfo<void> |
60 | { |
61 | public: |
62 | enum { |
63 | isPointer [[deprecated("Use std::is_pointer instead" )]] = false, |
64 | isIntegral [[deprecated("Use std::is_integral instead" )]] = false, |
65 | isComplex = false, |
66 | isRelocatable = false, |
67 | isValueInitializationBitwiseZero = false, |
68 | }; |
69 | }; |
70 | |
71 | /*! |
72 | \class QTypeInfoMerger |
73 | \inmodule QtCore |
74 | \internal |
75 | |
76 | \brief QTypeInfoMerger merges the QTypeInfo flags of T1, T2... and presents them |
77 | as a QTypeInfo<T> would do. |
78 | |
79 | Let's assume that we have a simple set of structs: |
80 | |
81 | \snippet code/src_corelib_global_qglobal.cpp 50 |
82 | |
83 | To create a proper QTypeInfo specialization for A struct, we have to check |
84 | all sub-components; B, C and D, then take the lowest common denominator and call |
85 | Q_DECLARE_TYPEINFO with the resulting flags. An easier and less fragile approach is to |
86 | use QTypeInfoMerger, which does that automatically. So struct A would have |
87 | the following QTypeInfo definition: |
88 | |
89 | \snippet code/src_corelib_global_qglobal.cpp 51 |
90 | */ |
91 | template <class T, class...Ts> |
92 | class QTypeInfoMerger |
93 | { |
94 | static_assert(sizeof...(Ts) > 0); |
95 | public: |
96 | static constexpr bool isComplex = ((QTypeInfo<Ts>::isComplex) || ...); |
97 | static constexpr bool isRelocatable = ((QTypeInfo<Ts>::isRelocatable) && ...); |
98 | [[deprecated("Use std::is_pointer instead" )]] static constexpr bool isPointer = false; |
99 | [[deprecated("Use std::is_integral instead" )]] static constexpr bool isIntegral = false; |
100 | static constexpr bool isValueInitializationBitwiseZero = false; |
101 | static_assert(!isRelocatable || |
102 | std::is_copy_constructible_v<T> || |
103 | std::is_move_constructible_v<T>, |
104 | "All Ts... are Q_RELOCATABLE_TYPE, but T is neither copy- nor move-constructible, " |
105 | "so cannot be Q_RELOCATABLE_TYPE. Please mark T as Q_COMPLEX_TYPE manually." ); |
106 | }; |
107 | |
108 | // QTypeInfo for std::pair: |
109 | // std::pair is spec'ed to be struct { T1 first; T2 second; }, so, unlike tuple<>, |
110 | // we _can_ specialize QTypeInfo for pair<>: |
111 | template <class T1, class T2> |
112 | class QTypeInfo<std::pair<T1, T2>> : public QTypeInfoMerger<std::pair<T1, T2>, T1, T2> {}; |
113 | |
114 | #define Q_DECLARE_MOVABLE_CONTAINER(CONTAINER) \ |
115 | template <typename ...T> \ |
116 | class QTypeInfo<CONTAINER<T...>> \ |
117 | { \ |
118 | public: \ |
119 | enum { \ |
120 | isPointer [[deprecated("Use std::is_pointer instead")]] = false, \ |
121 | isIntegral [[deprecated("Use std::is_integral instead")]] = false, \ |
122 | isComplex = true, \ |
123 | isRelocatable = true, \ |
124 | isValueInitializationBitwiseZero = false, \ |
125 | }; \ |
126 | } |
127 | |
128 | Q_DECLARE_MOVABLE_CONTAINER(QList); |
129 | Q_DECLARE_MOVABLE_CONTAINER(QQueue); |
130 | Q_DECLARE_MOVABLE_CONTAINER(QStack); |
131 | Q_DECLARE_MOVABLE_CONTAINER(QSet); |
132 | Q_DECLARE_MOVABLE_CONTAINER(QMap); |
133 | Q_DECLARE_MOVABLE_CONTAINER(QMultiMap); |
134 | Q_DECLARE_MOVABLE_CONTAINER(QHash); |
135 | Q_DECLARE_MOVABLE_CONTAINER(QMultiHash); |
136 | Q_DECLARE_MOVABLE_CONTAINER(QCache); |
137 | |
138 | #undef Q_DECLARE_MOVABLE_CONTAINER |
139 | |
140 | /* |
141 | Specialize a specific type with: |
142 | |
143 | Q_DECLARE_TYPEINFO(type, flags); |
144 | |
145 | where 'type' is the name of the type to specialize and 'flags' is |
146 | logically-OR'ed combination of the flags below. |
147 | */ |
148 | enum { /* TYPEINFO flags */ |
149 | Q_COMPLEX_TYPE = 0, |
150 | Q_PRIMITIVE_TYPE = 0x1, |
151 | Q_RELOCATABLE_TYPE = 0x2, |
152 | Q_MOVABLE_TYPE = 0x2, |
153 | Q_DUMMY_TYPE = 0x4, |
154 | }; |
155 | |
156 | #define Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS) \ |
157 | class QTypeInfo<TYPE > \ |
158 | { \ |
159 | public: \ |
160 | enum { \ |
161 | isComplex = (((FLAGS) & Q_PRIMITIVE_TYPE) == 0) && !std::is_trivial_v<TYPE>, \ |
162 | isRelocatable = !isComplex || ((FLAGS) & Q_RELOCATABLE_TYPE) || QtPrivate::qIsRelocatable<TYPE>, \ |
163 | isPointer [[deprecated("Use std::is_pointer instead")]] = std::is_pointer_v< TYPE >, \ |
164 | isIntegral [[deprecated("Use std::is_integral instead")]] = std::is_integral< TYPE >::value, \ |
165 | isValueInitializationBitwiseZero = QtPrivate::qIsValueInitializationBitwiseZero<TYPE>, \ |
166 | }; \ |
167 | static_assert(!isRelocatable || \ |
168 | std::is_copy_constructible_v<TYPE > || \ |
169 | std::is_move_constructible_v<TYPE >, \ |
170 | #TYPE " is neither copy- nor move-constructible, so cannot be Q_RELOCATABLE_TYPE"); \ |
171 | } |
172 | |
173 | #define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \ |
174 | template<> \ |
175 | Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS) |
176 | |
177 | /* Specialize QTypeInfo for QFlags<T> */ |
178 | template<typename T> class QFlags; |
179 | template<typename T> |
180 | Q_DECLARE_TYPEINFO_BODY(QFlags<T>, Q_PRIMITIVE_TYPE); |
181 | |
182 | namespace QTypeTraits |
183 | { |
184 | |
185 | /* |
186 | The templates below aim to find out whether one can safely instantiate an operator==() or |
187 | operator<() for a type. |
188 | |
189 | This is tricky for containers, as most containers have unconstrained comparison operators, even though they |
190 | rely on the corresponding operators for its content. |
191 | This is especially true for all of the STL template classes that have a comparison operator defined, and |
192 | leads to the situation, that the compiler would try to instantiate the operator, and fail if any |
193 | of its template arguments does not have the operator implemented. |
194 | |
195 | The code tries to cover the relevant cases for Qt and the STL, by checking (recusrsively) the value_type |
196 | of a container (if it exists), and checking the template arguments of pair, tuple and variant. |
197 | */ |
198 | namespace detail { |
199 | |
200 | // find out whether T is a conteiner |
201 | // this is required to check the value type of containers for the existence of the comparison operator |
202 | template <typename, typename = void> |
203 | struct is_container : std::false_type {}; |
204 | template <typename T> |
205 | struct is_container<T, std::void_t< |
206 | typename T::value_type, |
207 | std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool> |
208 | >> : std::true_type {}; |
209 | |
210 | |
211 | // Checks the existence of the comparison operator for the class itself |
212 | QT_WARNING_PUSH |
213 | QT_WARNING_DISABLE_FLOAT_COMPARE |
214 | template <typename, typename = void> |
215 | struct has_operator_equal : std::false_type {}; |
216 | template <typename T> |
217 | struct has_operator_equal<T, std::void_t<decltype(bool(std::declval<const T&>() == std::declval<const T&>()))>> |
218 | : std::true_type {}; |
219 | QT_WARNING_POP |
220 | |
221 | // Two forward declarations |
222 | template<typename T, bool = is_container<T>::value> |
223 | struct expand_operator_equal_container; |
224 | template<typename T> |
225 | struct expand_operator_equal_tuple; |
226 | |
227 | // the entry point for the public method |
228 | template<typename T> |
229 | using expand_operator_equal = expand_operator_equal_container<T>; |
230 | |
231 | // if T isn't a container check if it's a tuple like object |
232 | template<typename T, bool> |
233 | struct expand_operator_equal_container : expand_operator_equal_tuple<T> {}; |
234 | // if T::value_type exists, check first T::value_type, then T itself |
235 | template<typename T> |
236 | struct expand_operator_equal_container<T, true> : |
237 | std::conjunction< |
238 | std::disjunction< |
239 | std::is_same<T, typename T::value_type>, // avoid endless recursion |
240 | expand_operator_equal<typename T::value_type> |
241 | >, expand_operator_equal_tuple<T>> {}; |
242 | |
243 | // recursively check the template arguments of a tuple like object |
244 | template<typename ...T> |
245 | using expand_operator_equal_recursive = std::conjunction<expand_operator_equal<T>...>; |
246 | |
247 | template<typename T> |
248 | struct expand_operator_equal_tuple : has_operator_equal<T> {}; |
249 | template<typename T> |
250 | struct expand_operator_equal_tuple<std::optional<T>> : expand_operator_equal_recursive<T> {}; |
251 | template<typename T1, typename T2> |
252 | struct expand_operator_equal_tuple<std::pair<T1, T2>> : expand_operator_equal_recursive<T1, T2> {}; |
253 | template<typename ...T> |
254 | struct expand_operator_equal_tuple<std::tuple<T...>> : expand_operator_equal_recursive<T...> {}; |
255 | template<typename ...T> |
256 | struct expand_operator_equal_tuple<std::variant<T...>> : expand_operator_equal_recursive<T...> {}; |
257 | |
258 | // the same for operator<(), see above for explanations |
259 | template <typename, typename = void> |
260 | struct has_operator_less_than : std::false_type{}; |
261 | template <typename T> |
262 | struct has_operator_less_than<T, std::void_t<decltype(bool(std::declval<const T&>() < std::declval<const T&>()))>> |
263 | : std::true_type{}; |
264 | |
265 | template<typename T, bool = is_container<T>::value> |
266 | struct expand_operator_less_than_container; |
267 | template<typename T> |
268 | struct expand_operator_less_than_tuple; |
269 | |
270 | template<typename T> |
271 | using expand_operator_less_than = expand_operator_less_than_container<T>; |
272 | |
273 | template<typename T, bool> |
274 | struct expand_operator_less_than_container : expand_operator_less_than_tuple<T> {}; |
275 | template<typename T> |
276 | struct expand_operator_less_than_container<T, true> : |
277 | std::conjunction< |
278 | std::disjunction< |
279 | std::is_same<T, typename T::value_type>, |
280 | expand_operator_less_than<typename T::value_type> |
281 | >, expand_operator_less_than_tuple<T> |
282 | > {}; |
283 | |
284 | template<typename ...T> |
285 | using expand_operator_less_than_recursive = std::conjunction<expand_operator_less_than<T>...>; |
286 | |
287 | template<typename T> |
288 | struct expand_operator_less_than_tuple : has_operator_less_than<T> {}; |
289 | template<typename T> |
290 | struct expand_operator_less_than_tuple<std::optional<T>> : expand_operator_less_than_recursive<T> {}; |
291 | template<typename T1, typename T2> |
292 | struct expand_operator_less_than_tuple<std::pair<T1, T2>> : expand_operator_less_than_recursive<T1, T2> {}; |
293 | template<typename ...T> |
294 | struct expand_operator_less_than_tuple<std::tuple<T...>> : expand_operator_less_than_recursive<T...> {}; |
295 | template<typename ...T> |
296 | struct expand_operator_less_than_tuple<std::variant<T...>> : expand_operator_less_than_recursive<T...> {}; |
297 | |
298 | } |
299 | |
300 | template<typename T, typename = void> |
301 | struct is_dereferenceable : std::false_type {}; |
302 | |
303 | template<typename T> |
304 | struct is_dereferenceable<T, std::void_t<decltype(std::declval<T>().operator->())> > |
305 | : std::true_type {}; |
306 | |
307 | template <typename T> |
308 | inline constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value; |
309 | |
310 | template<typename T> |
311 | struct has_operator_equal : detail::expand_operator_equal<T> {}; |
312 | template<typename T> |
313 | inline constexpr bool has_operator_equal_v = has_operator_equal<T>::value; |
314 | |
315 | template <typename Container, typename T> |
316 | using has_operator_equal_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_equal<T>>; |
317 | |
318 | template<typename T> |
319 | struct has_operator_less_than : detail::expand_operator_less_than<T> {}; |
320 | template<typename T> |
321 | inline constexpr bool has_operator_less_than_v = has_operator_less_than<T>::value; |
322 | |
323 | template <typename Container, typename T> |
324 | using has_operator_less_than_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_less_than<T>>; |
325 | |
326 | template <typename ...T> |
327 | using compare_eq_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal<T>...>, bool>; |
328 | |
329 | template <typename Container, typename ...T> |
330 | using compare_eq_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal_container<Container, T>...>, bool>; |
331 | |
332 | template <typename ...T> |
333 | using compare_lt_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than<T>...>, bool>; |
334 | |
335 | template <typename Container, typename ...T> |
336 | using compare_lt_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than_container<Container, T>...>, bool>; |
337 | |
338 | namespace detail { |
339 | |
340 | template<typename T> |
341 | const T &const_reference(); |
342 | template<typename T> |
343 | T &reference(); |
344 | |
345 | } |
346 | |
347 | template <typename Stream, typename, typename = void> |
348 | struct has_ostream_operator : std::false_type {}; |
349 | template <typename Stream, typename T> |
350 | struct has_ostream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() << detail::const_reference<T>())>> |
351 | : std::true_type {}; |
352 | template <typename Stream, typename T> |
353 | inline constexpr bool has_ostream_operator_v = has_ostream_operator<Stream, T>::value; |
354 | |
355 | template <typename Stream, typename Container, typename T> |
356 | using has_ostream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_ostream_operator<Stream, T>>; |
357 | |
358 | template <typename Stream, typename, typename = void> |
359 | struct has_istream_operator : std::false_type {}; |
360 | template <typename Stream, typename T> |
361 | struct has_istream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() >> detail::reference<T>())>> |
362 | : std::true_type {}; |
363 | template <typename Stream, typename T> |
364 | inline constexpr bool has_istream_operator_v = has_istream_operator<Stream, T>::value; |
365 | template <typename Stream, typename Container, typename T> |
366 | using has_istream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_istream_operator<Stream, T>>; |
367 | |
368 | template <typename Stream, typename T> |
369 | inline constexpr bool has_stream_operator_v = has_ostream_operator_v<Stream, T> && has_istream_operator_v<Stream, T>; |
370 | |
371 | } |
372 | |
373 | |
374 | QT_END_NAMESPACE |
375 | #endif // QTYPEINFO_H |
376 | |