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

source code of qtbase/src/corelib/global/qtypeinfo.h